Oracle : how to subtract two dates and get minutes of the result - oracle

I wrote this function to get minutes from a date, but I cannot get minutes between two dates, How to get that ?
FUNCTION get_minute(p_date DATE)
RETURN NUMBER
IS
BEGIN
IF p_date IS NOT NULL THEN
return EXTRACT(MINUTE FROM TO_TIMESTAMP(to_char(p_date,'DD-MON-YYYY HH:MI:SS'),'DD-MON-YYYY HH24:MI:SS'));
ELSE
RETURN 0;
END IF;
END get_minute;

When you subtract two dates in Oracle, you get the number of days between the two values. So you just have to multiply to get the result in minutes instead:
SELECT (date2 - date1) * 24 * 60 AS minutesBetween
FROM ...

For those who want to substrat two timestamps (instead of dates), there is a similar solution:
SELECT ( CAST( date2 AS DATE ) - CAST( date1 AS DATE ) ) * 1440 AS minutesInBetween
FROM ...
or
SELECT ( CAST( date2 AS DATE ) - CAST( date1 AS DATE ) ) * 86400 AS secondsInBetween
FROM ...

I think you can adapt the function to substract the two timestamps:
return EXTRACT(MINUTE FROM
TO_TIMESTAMP(to_char(p_date1,'DD-MON-YYYY HH:MI:SS'),'DD-MON-YYYY HH24:MI:SS')
-
TO_TIMESTAMP(to_char(p_date2,'DD-MON-YYYY HH:MI:SS'),'DD-MON-YYYY HH24:MI:SS')
);
I think you could simplify it by just using CAST(p_date as TIMESTAMP).
return EXTRACT(MINUTE FROM cast(p_date1 as TIMESTAMP) - cast(p_date2 as TIMESTAMP));
Remember dates and timestamps are big ugly numbers inside Oracle, not what we see in the screen; we don't need to tell him how to read them.
Also remember timestamps can have a timezone defined; not in this case.

I can handle this way:
select to_number(to_char(sysdate,'MI')) - to_number(to_char(*YOUR_DATA_VALUE*,'MI')),max(exp_time) from ...
Or
if you want to the hour just change the MI;
select to_number(to_char(sysdate,'HH24')) - to_number(to_char(*YOUR_DATA_VALUE*,'HH24')),max(exp_time) from ...
the others don't work for me.
Good luck.

Related

Oracle get all records between current day inserted with timestamp

Unable to retrieve records properly with the formatted date values, need select query with right date format to get all records inserted per day
I have a date string like this in my script -  
dateString :='26-MAR-20 05.00.00.00000000 AM';
I want to add 0.313 minutes to this date value. And also I want to
increment it to the next day something like this- '27-MAR-20
05.00.00.00000000 AM'; ​
I tried this 
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
​
I can see the output and inserted these values into the DB but
unable to retrieve records properly with these formatted date values..
I suspect it might be to do with the timestamp fields
When I run this query -
select *from rptallexceptions where exceptiontime between '27-MAR-2020 04.00.00.000000000 AM' and '28-MAR-2020 03.59.00.000000000 AM' order by exceptiontime desc;
​
--- this one gives 3833 records but expected is 4600
it shows only the records on 27th march., records inserted with timestamp containing 28th march are not retrieved..
select *from rptallexceptions where exceptiontime between '28-MAR-2020 04.00.00.000000000 AM' and '29-MAR-2020 03.59.00.000000000 AM' order by exceptiontime desc;
--- this one returns '0' rows
Excerpt from the script:
cnt :=cnt +1;
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
-- add time difference for each exception.
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
-- after n*4600 exceptions,update date to next date.
IF REMAINDER(cnt,exceptionsPerDay) = 0 THEN
dateField := to_timestamp(dateField + 1,'DD/MON/YYYY HH24:MI:SS.FF');
END IF;
The way I see it, your problem is that you're comparing timestamps to strings.
Sample table:
SQL> create table test as
2 select to_timestamp('27.03.2020 04:00:00:000000', 'dd.mm.yyyy hh24:mi:ss:ff6') datum, 'A' name from dual
3 union all
4 select to_timestamp('28.03.2020 15:23:00:000000', 'dd.mm.yyyy hh24:mi:ss:ff6') datum, 'A' name from dual;
Table created.
Query should use timestamp, not string:
SQL> select *
2 from test
3 where datum between to_timestamp('25.03.2020 12:00:00:000000am', 'dd.mm.yyyy hh:mi:ss:ff6am') --> timestamp
4 and to_timestamp('28.03.2020 12:00:00:000000am', 'dd.mm.yyyy hh:mi:ss:ff6am'); --> timestamp
DATUM N
--------------------------------------------------------------------------- -
27.03.20 04:00:00,000000000 A
SQL>
You were kind of "lucky" (though, one might call it bad luck as your query didn't fail, but it produced wrong results, according to number of rows being returned) for not getting an error as my database raises (due to different NLS settings):
SQL> select *
2 from test
3 where datum between '25.03.2020 12:00:00:000000am' --> string
4 and '28.03.2020 12:00:00:000000am'; --> string
where datum between '25.03.2020 12:00:00:000000am'
*
ERROR at line 3:
ORA-01830: date format picture ends before converting entire input string
SQL>
Strings are handled differently than dates (or timestamps), or numbers. Maybe you'll see the difference clearly in this example:
SQL> with test (col) as
2 (select '1' from dual union all
3 select '9' from dual union all
4 select '20' from dual
5 )
6 select *
7 from test
8 where col < '9';
CO
--
1
20
SQL>
20 < 9? Yes, if those are strings. The same might have happened to you. Try with proper datatype.
When you do:
dateString :=to_char(dateField,'DD-MON-YYYY HH24:MI:SS.FF');
you are converting whatever is the initial value of dateField to a string; but it looks like that has has not been set yet, so you end up with an empty string (which is the same as null). Your original value of dateString is never being used.
If you skip that and just do:
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateField := to_timestamp(dateString, 'DD/MON/YYYY HH24:MI:SS.FF') + 0.313 * INTERVAL '1' MINUTE;
​then you are converting the 2-digit year 20 with a YYYY format element, which will turn it into year 0020, not 2020; but you also don't have AM in the format model, so it will get ORA-01830 anyway, and have HH24 instead of HH. I think your to_char() is attempting to 'correct' the string format, but that's not a great approach.
If you can't control the starting string format then the model has to match that:
dateString :='26-MAR-20 05.00.00.00000000 AM';
dateField := to_timestamp(dateString, 'DD-MON-RR HH:MI:SS.FF AM') + 0.313 * INTERVAL '1' MINUTE;
which gives dateField a value of 2020-03-26 05:00:18.780000.
Later on you do:
dateField := to_timestamp(dateField + 1,'DD/MON/YYYY HH24:MI:SS.FF');
which is also doing an implicit conversion of dateField + 1 - which is converted to a date, incidentally as timestamp + number is a date, not a timestamp - to a string; which will use your NLS_DATE_FORMAT. Presumably that is something like 'DD-MON-YYYY'.
So stepping through that:
dateField + 1 => date '2020-03-27 05:00:18' (losing fractional seconds)
implicit to_char(dateField + 1) => string '27-Mar-2020' (losing time)
to_timestamp(to_char(dateField + 1), '...') => timestamp '2020-03-27 00:00:00.000000' (with time at midnight).
So your between is, at best going to find records between 2020-03-26 05:00:18.780000 and 2020-03-27 00:00:00.0000000 - so it won't pick up any records later than midnight on the 27th.
Don't convert to and from strings when you don't need to; leave data in its native data type (timestamp in this case) and use direct manipulation with intervals.
Using between isn't ideal anyway, because it is inclusive; it would be better to end up with:
exceptiontime >= timestamp '2020-03-26 05:00:18.780000'
and exceptionTime < timestamp '2020-03-27 05:00:18.780000'

Trying to calculate difference between two date in format (00-00-0000 00:00:00 ) in minutes in oracle

I have date in following format a_time=01-06-2020 15:28:06 & b_time=01-06-2020 15:39:00 in table t_mst
and i want to convert diff of given values in queries in minutes
select (extract( day from diff )*24*60*60 +
extract( hour from diff )*60*60 +
extract( minute from diff )*60 +
extract( second from diff ))/60 total_Minutes
from(select b_time - a_time diff
from t_mst Where a_time between (trunc(sysdate, 'mm')) and (sysdate))
But getting Error "Invalid extract field for extract source"
any idea would be appreciated.
The error you show means that b_time - a_time worked. Which means they are in date (or perhaps timestamp) data type; that is good.
Then, they must be date; if they were timestamp then the difference would be interval data type, and you can indeed extract time elements from interval. So, the error message suggests that the data type of a_time and b_time is indeed date.
Now: In Oracle, the difference between two dates is a number (in days); you can't extract time elements from a number. Instead, consider that the difference is already a number of days (including a fractional part); to convert it to minutes, you only have to multiply it by 1440 - and then round the result, if needed.
You have tagged with 3 databases and the code is for postgreSQL. I assume you meant, postgreSQL:
select (extract( epoch from age(a_time, b_time)/60) total_Minutes
from t_mst
--Where a_time between (trunc(sysdate, 'mm')) and (sysdate))
If you need it casted to int:
select (extract( epoch from age(a_time, b_time)/60)::int total_Minutes
from t_mst
PS: Not sure you meant plSQL or PL\pgSQL.
If b_timeand a_time data types are DATE you can simply run
select (b_time - a_time) * 24*60 AS total_Minutes
from t_mst
Where a_time between trunc(sysdate, 'mm') and sysdate
In case they are TIMESTAMP data type you have to extract the values, e.g. like this:
select
EXTRACT(DAY FROM (b_time - a_time))*24*60
+ EXTRACT(HOUR FROM (b_time - a_time))*60
+ EXTRACT(MINUTE FROM (b_time - a_time))
+ EXTRACT(SECOND FROM (b_time - a_time))/60
AS total_Minutes
from t_mst
Where a_time between trunc(sysdate, 'mm') and sysdate

How to Add an Interval to a Date in Oracle

I'm trying to get a new date from the product of 'date' + 'time interval'.
Something like this.
'15/02/2016 18:00:00' + '+00 02:00:00.000000'
Expected result:
'15/02/2016 20:00:00'
But using the columns in database.
CREATE TABLE timerest
(
DATE_ASIGN DATE,
TIME_ASIGN INTERVAL DAY(2) TO SECOND(0)
);
Thanks for your help.
You can just add them together:
insert into timerest (date_asign, time_asign)
values (to_date('15/02/2016 18:00:00', 'DD/MM/YYYY HH24:MI:SS'),
to_dsinterval('+00 02:00:00.000000'));
alter session set NLS_DATE_FORMAT = 'DD/MM/YYYY HH24:MI:SS';
select date_asign + time_asign from timerest;
DATE_ASIGN+TIME_ASIGN
---------------------
15/02/2016 20:00:00
This follows the rules for datetime/interval arithmetic: date + interval = date.
If you have a date in DATE format you can simply add the a numeric interval that represents days (for example 1.5 is 1 day and a half)
You can extract from the time interval days and hours and then add them to to you date because, if I remember correctly, you can't add directly them to a date type (maybe to a timestamp type you can)
To extract the days you can use the extract function:
(
extract(second from TIME_ASIGN)/3600)+(extract(hour from TIME_ASIGN)/24)+(extract(day from TIME_ASIGN)/24)
then you add the number to your DATE_ASIGN

In Oracle, is there a function that calculates the difference between two Dates?

In Oracle, is there a function that calculates the difference between two Dates? If not, is a way to display the difference between two dates in hours and minutes?
Query:
SELECT Round(max((EndDate - StartDate ) * 24), 2) as MaximumScheduleTime,
Round(min((EndDate - StartDate) * 24), 2) as MinimumScheduleTime,
Round(avg((EndDate - StartDate) * 24), 2) as AveragegScheduleTime
FROM table1
You can subtract two dates in Oracle. The result is a FLOAT which represents the number of days between the two dates. You can do simple arithmetic on the fractional part to calculate the hours, minutes and seconds.
Here's an example:
SELECT TO_DATE('2000/01/02:12:00:00PM', 'yyyy/mm/dd:hh:mi:ssam')-TO_DATE('2000/01/01:12:00:00AM', 'yyyy/mm/dd:hh:mi:ssam') DAYS FROM DUAL
Results in: 1.5
You can use these functions :
1) EXTRACT(element FROM temporal_value)
2) NUMTOYMINTERVAL (n, unit)
3) NUMTODSINTERVAL (n, unit).
For example :
SELECT EXTRACT(DAY FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
|| ' days ' ||
EXTRACT(HOUR FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
||':'||
EXTRACT(MINUTE FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
||':'||
EXTRACT(SECOND FROM NUMTODSINTERVAL(end_time - start_time, 'DAY'))
"Lead Time"
FROM table;
With Oracle Dates, this is pretty
trivial, you can get either TOTAL
(days, hours, minutes, seconds)
between 2 dates simply by subtracting
them or with a little mod'ing you can
get Days/Hours/Minutes/Seconds
between.
http://asktom.oracle.com/tkyte/Misc/DateDiff.html
Also, from the above link:
If you really want 'datediff' in your
database, you can just do something
like this:
SQL> create or replace function datediff( p_what in varchar2,
2 p_d1 in date,
3 p_d2 in date ) return number
4 as
5 l_result number;
6 begin
7 select (p_d2-p_d1) *
8 decode( upper(p_what),
9 'SS', 24*60*60, 'MI', 24*60, 'HH', 24, NULL )
10 into l_result from dual;
11
11 return l_result;
12 end;
13 /
Function created
Q: In Oracle, is there a function that calculates the difference between two Dates?
Just subtract one date expression from another to get the difference expressed as a number of days. The integer portion is the number of whole days, the fractional portion is the fraction of a day. Simple arithmetic after that, multiply by 24 to get hours.
Q: If not, is a way to display the difference between two dates in hours and minutes?
It's just a matter of expressing the duration as whole hours and remainder minutes.
We can go "old school" to get durations in hhhh:mi format using a combination of simple builtin functions:
SELECT decode(sign(t.maxst),-1,'-','')||to_char(floor(abs(t.maxst)/60))||
decode(t.maxst,null,'',':')||to_char(mod(abs(t.maxst),60),'FM00')
as MaximumScheduleTime
, decode(sign(t.minst),-1,'-','')||to_char(floor(abs(t.minst)/60))||
decode(t.minst,null,'',':')||to_char(mod(abs(t.minst),60),'FM00')
as MinimumScheduleTime
, decode(sign(t.avgst),-1,'-','')||to_char(floor(abs(t.avgst)/60))
decode(t.avgst,null,'',':')||to_char(mod(abs(t.avgst),60),'FM00')
as AverageScheduleTime
FROM (
SELECT round(max((EndDate - StartDate) *1440),0) as maxst
, round(min((EndDate - StartDate) *1440),0) as minst
, round(avg((EndDate - StartDate) *1440),0) as avgst
FROM table1
) t
Yeah, it's fugly, but it's pretty fast. Here's a simpler case, that shows better what's going on:
select dur as "minutes"
, abs(dur) as "unsigned_minutes"
, floor(abs(dur)/60) as "unsigned_whole_hours"
, to_char(floor(abs(dur)/60)) as "hhhh"
, mod(abs(dur),60) as "unsigned_remainder_minutes"
, to_char(mod(abs(dur),60),'FM00') as "mi"
, decode(sign(dur),-1,'-','') as "leading_sign"
, decode(dur,null,'',':') as "colon_separator"
from (select round(( date_expr1 - date_expr2 )*24*60,0) as dur
from ...
)
(replace date_expr1 and date_expr2 with date expressions)
let's unpack this
date_expr1 - date_expr2 returns difference in number of days
multiply by 1440 (24*60) to get duration in minutes
round (or floor) to resolve fractional minutes into integer minutes
divide by 60, integer quotient is hours, remainder is minutes
abs function to get absolute value (change negative values to positive)
to_char format model FM00 give two digits (leading zeros)
use decode function to format a negative sign and a colon (if needed)
The SQL statement could be made less ugly using a PL/SQL function, one that takes two DATE arguments a duration in (fractional) days and returns formatted hhhh:mi
(untested)
create function hhhhmi(an_dur in number)
return varchar2 deterministic
is
begin
if an_dur is null then
return null;
end if;
return decode(sign(an_dur),-1,'-','')
|| to_char(floor(abs(an_dur)*24))
||':'||to_char(mod((abs(an_dur)*1440),60),'FM00');
end;
With the function defined:
SELECT hhhhmi(max(EndDate - StartDate)) as MaximumScheduleTime
, hhhhmi(min(EndDate - StartDate)) as MinimumScheduleTime
, hhhhmi(avg(EndDate - StartDate)) as AverageScheduleTime
FROM table1
You can use the months_between function to convert dates to the difference in years and then use between the decimal years you are interested:
CASE
WHEN ( ( MONTHS_BETWEEN( TO_DATE(date1, 'YYYYMMDD'),
TO_DATE(date1,'YYYYMMDD'))/12
)
BETWEEN Age1DecimalInYears AND Age2DecimalInYears
)
THEN 'It is between the two dates'
ELSE 'It is not between the two dates'
END;
You may need to change date format to match the a given date format and verify that 31 day months work for your specific scenarios.
References:
( found on www on 05/15/2015 )
1. Oracle/PLSQL: MONTHS_BETWEEN Function
2. Oracle Help Center - MONTHS_BETWEEN

Oracle: how to add minutes to a timestamp?

I need to add 30 minutes to values in a Oracle date column. I do this in my SELECT statement by specifying
to_char(date_and_time + (.000694 * 31)
which works fine most of the time. But not when the time is on the AM/PM border. For example, adding 30 minutes to 12:30 [which is PM] returns 1:00 which is AM. The answer I expect is 13:00. What's the correct way to do this?
In addition to being able to add a number of days to a date, you can use interval data types assuming you are on Oracle 9i or later, which can be somewhat easier to read,
SQL> ed
Wrote file afiedt.buf
SELECT sysdate, sysdate + interval '30' minute FROM dual
SQL> /
SYSDATE SYSDATE+INTERVAL'30'
-------------------- --------------------
02-NOV-2008 16:21:40 02-NOV-2008 16:51:40
All of the other answers are basically right but I don't think anyone's directly answered your original question.
Assuming that "date_and_time" in your example is a column with type DATE or TIMESTAMP, I think you just need to change this:
to_char(date_and_time + (.000694 * 31))
to this:
to_char(date_and_time + (.000694 * 31), 'DD-MON-YYYY HH24:MI')
It sounds like your default date format uses the "HH" code for the hour, not "HH24".
Also, I think your constant term is both confusing and imprecise. I guess what you did is calculate that (.000694) is about the value of a minute, and you are multiplying it by the number of minutes you want to add (31 in the example, although you said 30 in the text).
I would also start with a day and divide it into the units you want within your code. In this case, (1/48) would be 30 minutes; or if you wanted to break it up for clarity, you could write ( (1/24) * (1/2) ).
This would avoid rounding errors (except for those inherent in floating point which should be meaningless here) and is clearer, at least to me.
UPDATE "TABLE"
SET DATE_FIELD = CURRENT_TIMESTAMP + interval '48' minute
WHERE (...)
Where interval is one of
YEAR
MONTH
DAY
HOUR
MINUTE
SECOND
from http://www.orafaq.com/faq/how_does_one_add_a_day_hour_minute_second_to_a_date_value
The SYSDATE pseudo-column shows the current system date and time. Adding 1 to SYSDATE will advance the date by 1 day. Use fractions to add hours, minutes or seconds to the date
SQL> select sysdate, sysdate+1/24, sysdate +1/1440, sysdate + 1/86400 from dual;
SYSDATE SYSDATE+1/24 SYSDATE+1/1440 SYSDATE+1/86400
-------------------- -------------------- -------------------- --------------------
03-Jul-2002 08:32:12 03-Jul-2002 09:32:12 03-Jul-2002 08:33:12 03-Jul-2002 08:32:13
I prefer using an interval literal for this, because interval '30' minute or interval '5' second is a lot easier to read then 30 / (24 * 60) or 5 / (24 * 60 * 69)
e.g.
some_date + interval '2' hour
some_date + interval '30' minute
some_date + interval '5' second
some_date + interval '2' day
You can also combine several units into one expression:
some_date + interval '2 3:06' day to minute
Adds 2 days, 3 hours and 6 minutes to the date value
The above is also standard SQL and also works in several other DBMS.
More details in the manual: https://docs.oracle.com/database/121/SQLRF/sql_elements003.htm#SQLRF00221
If the data type of the field is date or timestamp, Oracle should always give the correct result if you add the correct number given in number of days (or a the correct fraction of a day in your case). So if you are trying to bump the value in 30 minutes, you should use :
select field + 0.5/24 from table;
Based on the information you provided, I believe this is what you tried to do and I am quite sure it works.
Can we not use this
SELECT date_and_time + INTERVAL '20:00' MINUTE TO SECOND FROM dual;
I am new to this domain.
like that very easily
i added 10 minutes to system date and always in preference use the Db server functions not custom one .
select to_char(sysdate + NUMTODSINTERVAL(10,'MINUTE'),'DD/MM/YYYY HH24:MI:SS') from dual;
Be sure that Oracle understands that the starting time is PM, and to specify the HH24 format mask for the final output.
SELECT to_char((to_date('12:40 PM', 'HH:MI AM') + (1/24/60) * 30), 'HH24:MI') as time
FROM dual
TIME
---------
13:10
Note: the 'AM' in the HH:MI is just the placeholder for the AM/PM meridian indicator. Could be also 'PM'
Oracle now has new built in functions to do this:
select systimestamp START_TIME, systimestamp + NUMTODSINTERVAL(30, 'minute') end_time from dual
Based on what you're asking for, you want the HH24:MI format for to_char.
To edit Date in oracle you can try
select to_char(<columnName> + 5 / 24 + 30 / (24 * 60),
'DD/MM/RRRR hh:mi AM') AS <logicalName> from <tableName>
SELECT to_char(sysdate + (1/24/60) * 30, 'dd/mm/yy HH24:MI am') from dual;
simply you can use this with various date format....

Resources