Change Excel date number to Oracle date - oracle

I'm having date as 41293 in oracle, how can i show it in DD/MON/YYYY format?
If i copy pasted it in Excel and change it to date format, it shows 01/19/13
Please help me.

The value you have is the number of days since the 30th of December 1899. Try:
select to_char(
to_date('1899-12-30', 'YYYY-MM-DD') + 41293,
'DD/MON/YYYY') from dual

Quoting from Oracle forum:
You need a tool to do that, since format is to tell oracle what type of format you have on your date type in the spreadsheet. While you may not have opted to format the date in Excel, it will appear as a date in the previewer. Use the format from this as a guide to enter into the datatype panel.
so, if you have a date that looks like this in the previewer, 19-jan-2006, then your format for the data type panel if you choose to insert that column is going to be DD-MON-YYYY,
Option 1:
Try using the below functions
FUNCTION FROMEXCELDATETIME ( ACELLVALUE IN VARCHAR2 )
RETURN TIMESTAMP
IS
EXCEL_BASE_DATE_TIME CONSTANT TIMESTAMP
:= TO_TIMESTAMP ( '12/31/1899',
'mm/dd/yyyy' ) ;
VAL CONSTANT NUMBER
:= TO_NUMBER ( NULLIF ( TRIM ( ACELLVALUE ),
'0' ) ) ;
BEGIN
RETURN EXCEL_BASE_DATE_TIME
+ NUMTODSINTERVAL ( VAL
- CASE
WHEN VAL >= 60
THEN
1
ELSE
0
END,
'DAY' );
END;
FUNCTION TOEXCELDATETIME ( ATIMESTAMP IN TIMESTAMP )
RETURN VARCHAR2
IS
EXCEL_BASE_DATE_TIME CONSTANT TIMESTAMP
:= TO_TIMESTAMP ( '12/31/1899',
'mm/dd/yyyy' ) ;
DIF CONSTANT INTERVAL DAY ( 9 ) TO SECOND ( 9 )
:= ATIMESTAMP
- EXCEL_BASE_DATE_TIME ;
DAYS CONSTANT INTEGER := EXTRACT ( DAY FROM DIF );
BEGIN
RETURN CASE
WHEN DIF IS NULL
THEN
''
ELSE
TO_CHAR ( DAYS
+ CASE
WHEN DAYS >= 60
THEN
1
ELSE
0
END
+ ROUND ( ( EXTRACT ( HOUR FROM DIF )
+ ( EXTRACT ( MINUTE FROM DIF )
+ EXTRACT ( SECOND FROM DIF )
/ 60 )
/ 60 )
/ 24,
4 ) )
END;
END;
Option 2:
The excel function would be =TEXT(B2,"MM/DD/YY"), to convert an Excel date value stored in B2. Then try using the test character in Oracle
If considering 1900 Jan 1st as start date,
SELECT
TO_CHAR ( TO_DATE ( '1900-01-01',
'YYYY-MM-DD' )
+ 41293,
'DD/MON/YYYY' )
FROM
DUAL

Microsoft's Documentation
Excel stores dates as sequential serial numbers so that they can be used in calculations. January 1, 1900 is serial number 1, and January 1, 2008 is serial number 39448 because it is 39,447 days after January 1, 1900.
Excel has a bug feature where it considers 1900 to be a leap year and day 60 is 1900-02-29 but that day never existed and a correction needs to be applied for this erroneous day.
It does also state that:
Microsoft Excel correctly handles all other leap years, including century years that are not leap years (for example, 2100). Only the year 1900 is incorrectly handled.
Therefore only a single correction is required.
So:
Before 1900-03-01 you can use DATE '1899-12-31' + value.
On or after 1900-03-01 you can use DATE '1899-12-30' + value.
Which can be put into a CASE statement:
SELECT CASE
WHEN value >= 1 AND value < 60
THEN DATE '1899-12-31' + value
WHEN value >= 60 AND value < 61
THEN NULL
WHEN value >= 61
THEN DATE '1899-12-30' + value
END AS converted_date
FROM your_table

Related

floor while calculating between two dates is giving 0 in Oracle

I have two dates by which I am calculating no of years/months. For below 2 dates I am getting output as 0 as it should return 0.4 months.
Here is my query
select floor((months_between(to_date('2022-07-01T00:00:00+05:30'), to_date('2022-01-11T00:00:00+05:30', 'dd-mm-yy'))) /12)
from dual;
Please suggest what I am doing wrong here
The floor function:
returns the largest integer equal to or less than n
so there is no way it can return 0.4. The ceil function is the similar. Neither takes an argument allowing retention of decimal places. And you don't want to round it, as in your example that would give 0.5, not 0.4.
Fortunately you can use trunc, which does have a decimal-place argument:
The TRUNC (number) function returns n1 truncated to n2 decimal places.
So you want trunc(<difference between dates>, 1) to get retain 1 decimal place.
select trunc (
months_between(
CAST(TO_TIMESTAMP_TZ('2022-07-01T00:00:00+05:30','YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE),
CAST(TO_TIMESTAMP_TZ('2022-01-11T00:00:00+05:30','YYYY-MM-DD"T"HH24:MI:SSTZH:TZM') AS DATE)
) / 12
, 1
) as result
from dual;
.4
Here trunc behaves essentially as you would want floor(n1, n2) to if that existed; there is no equivalent for ceil, but you can work around that. The same method can be applied here too, but isn't needed; I've included it in this db<>fiddle for fun.
You want:
to use TO_TIMESTAMP_TZ and not TO_DATE
to use a format model that matches the timestamp format such as YYYY-MM-DD"T"HH24:MI:SSTZD
to use FLOOR before dividing by 12 if you want to find the number of full months.
select FLOOR(
MONTHS_BETWEEN(
to_timestamp_tz('2022-07-01T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD'),
to_timestamp_tz('2022-01-11T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
)
) / 12 AS full_months_diff
from dual;
Which outputs:
FULL_MONTHS_DIFF
.4166666666666666666666666666666666666667
Alternatively, you could use the difference between the timestamps as an INTERVAL YEAR TO MONTH data type:
select EXTRACT(
YEAR FROM
( to_timestamp_tz('2022-07-01T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
- to_timestamp_tz('2022-01-11T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
) YEAR TO MONTH
) AS years,
EXTRACT(
MONTH FROM
(to_timestamp_tz('2022-07-01T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
- to_timestamp_tz('2022-01-11T00:00:00+05:30', 'YYYY-MM-DD"T"HH24:MI:SSTZD')
) YEAR TO MONTH
) AS months
from dual;
YEARS
MONTHS
0
6
Which rounds up the number of months.
db<>fiddle here

create pl sql trigger

I am trying to write a Trigger for before insert to validate a case date.
The case date should be between 5 years before and 7 years after current date.
For example, in 2018 the case date should be from year 2013 to 2025. If the date is out of range the trigger should stop inserting data.
CREATE OR REPLACE TRIGGER ch
BEFORE INSERT
on CASE
FOR EACH ROW
DECLARE
CASN number;
BEGIN
SELECT COUNT(*)
INTO CASN
FROM CASE
WHERE :new.STARTDATE > SYSDATE;
IF (CASN > 0) THEN
RAISE_APPLICATION_ERROR(-20000,'Start DATE CANNOT Be GREATER than today's
date');
END IF;
END;
Here STARTDATE is column of CASE table
This trigger starts when start date is greater than today's date but I need it to run when it's out range as given above.
How do I add an specified interval to the sysdate, so that it could work for the above condition?
The logic you are using in your Trigger is completely wrong. You don't need to get the count from the table using :NEW.STARTDATE. This is something what you are looking for.
CREATE OR replace TRIGGER ch
BEFORE INSERT ON cases
FOR EACH ROW
BEGIN
IF ( :NEW.casedate < SYSDATE - INTERVAL '5' year
OR :NEW.casedate > SYSDATE + INTERVAL '7' year ) THEN
RAISE_APPLICATION_ERROR(-20000,
'CASE DATE should be in range: current date - 5 years and current date + 7 years')
;
END IF;
END;
/
EDIT : I have not added TRUNC on the dates because I'm not sure if you want to consider time component as well while considering date range.If you are ok with just considering days, you may use TRUNC(SYSDATE) in place of just SYSDATE. Modify it accordingly as per your business needs.
Another option is to use CHECK constraint. Although Oracle does not allow you to have
use SYSDATE in a check constraint definition, you may create another column( or reuse existing) that defaults to SYSDATE and apply check constraint on that.
ALTER TABLE CASES ADD ( CURR_DATE DATE DEFAULT SYSDATE );
ALTER TABLE CASES ADD CONSTRAINT
RANGE_CHECK CHECK( casedate > CURR_DATE - INTERVAL '5' YEAR
AND casedate < CURR_DATE + INTERVAL '7' YEAR) ENABLE;
Date arithmetic. Oracle Database enables you to perform arithmetic operations on dates and time stamps in several ways:
Add a numeric value to or subtract it from a date, as in SYSDATE + 7; Oracle Database treats the number as the number of days.
Add one date to or subtract it from another, as in l_hiredate - SYSDATE.
Use a built-in function to “move” a date by a specified number of months or to another date in a week.
Here are some examples of date arithmetic with a date and a number (assume in all cases that the l_date variable has been declared as DATE):
Set a local variable to tomorrow’s date:
l_date := SYSDATE + 1;
Move back one hour:
l_date := SYSDATE - 1/24;
Move ahead 10 seconds:
l_date := SYSDATE + 10 / (60 * 60 * 24);
When you add one date to or subtract it from another, the result is the number of days between the two. As a result, executing this block:
DECLARE
l_date1 DATE := SYSDATE;
l_date2 DATE := SYSDATE + 10;
BEGIN
DBMS_OUTPUT.put_line (
l_date2 - l_date1);
DBMS_OUTPUT.put_line (
l_date1 - l_date2);
END;
returns the following output:
10
-10

How to convert YY to YYYY ORACLE

How to convert YY to YYYY, example below, if I run example below I will receive for year '0007':
DECLARE
lv_promcode_txt VARCHAR(6) := 'A0807X';
lv_prommth_txt VARCHAR2(7);
lv_promyear_txt VARCHAR2(7);
BEGIN
lv_prommth_txt := SUBSTR(lv_promcode_txt, 2,2);
DBMS_OUTPUT.PUT_LINE('Month of promo code is: '||
TO_(TO_DATE(lv_prommth_TXT,'MM'), 'MONTH'));
lv_promyear_txt := SUBSTR(lv_promcode_txt, 4,2);
DBMS_OUTPUT.PUT_LINE('Year of promo code is: '||TO_CHAR(TO_DATE
(lv_promyear_txt, 'YYYY'),'YYYY'));
END;
Your format mask is wrong. Example:
select TO_CHAR(TO_DATE (SUBSTR('A0807X', 4,2), 'YY'),'YYYY') from dual
returns 2007
Replace your last DBMS_OUTPUT.PUT_LINE part of the code with
DBMS_OUTPUT.PUT_LINE('Year of promo code is ( Style 1 ) : '||TO_CHAR(TO_DATE(lv_promyear_txt, 'YYYY'),'YYYY'));
DBMS_OUTPUT.PUT_LINE('Year of promo code is ( Style 2 ) : '||TO_CHAR(TO_DATE(lv_promyear_txt, 'RRRR'),'YYYY'));
Just changing YYYY literal RRRR is enough in TO_DATE function.
This concept is related to year 2k problem.
For a date TO_DATE('18', 'RRRR') gives the result 2018 as year ( for
the current century, and the last two digits of years are between 00-49 ),
while
For a date TO_DATE('74', 'RRRR') gives the result 1974 as year ( for
the previous century, and the last two digits of years are between 50-99 )

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

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.

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

Resources