I have a table with field sourcefilename which has 5 records. Following are the records.
SN. SOURCEFILENAME
1. 20170215095453_1.Iredell Memorial Hospital Dental Eligibility.xls_INFREPT01.txt
2. Iredell Memorial Hospital Eligibility April 2017.xls_INFREPT01.txt
3. Iredell Memorial Hospital Eligibility March 2017.xls_INFREPT01.txt
4. Iredell Memorial Hospital Eligibility May 2017.xls_INFREPT01.txt
5. Iredell Memorial Hospital October 2016 Dental Eligibility.xls_EligData.txt
I just need to extract first 8 characters and check if its a valid date. If it is a valid date then return TRUE else FALSE.
I tried ISDATE function. Is there are any other alternatives?
SELECT DISTINCT SubStr(sourcefilename,1,8),
CASE WHEN isdate(SubStr(sourcefilename,1,8),'YYYYMMDD') = 1 THEN 'TRUE' ELSE 'FALSE' END FROM ai_4451_1_metl;
Oracle 12.2 provides a new function VALIDATE_CONVERSION. So far I never used it, but I assume it would be like this:
CASE VALIDATE_CONVERSION(SubStr(sourcefilename,1,8) AS DATE, 'YYYYMMDD')
WHEN 1 THEN 'TRUE'
ELSE 'FALSE'
END
If you have Oracle 12.2 you can use the validate_conversion function:
with demo as
( select '20170101' as sourcetest from dual union all
select '20171100' from dual )
select sourcetest
, validate_conversion(sourcetest as date, 'YYYYMMDD') as test_result
from demo;
SOURCETEST TEST_RESULT
----------- -----------
20170101 1
20171100 0
There is no Oracle built_in isdate() or differently named equivalent (in versions before 12cR2). But you can write your own:
create or replace function isdate
( p_date_str in varchar2
, p_date_fmt in varchar2 )
return varchar2
is
return_value varchar2(5);
l_date date;
begin
begin
l_date := to_date(p_date_str, p_date_fmt);
return_value := 'TRUE';
exception
when others then
return_value := 'FALSE';
end;
return return_value;
end isdate;
/
If casting the string to a DATE datatype succeeds it's a valid date, otherwise it isn't.
If your data quality issues are such that your strings have multiple date formats then you can implement this alternate solution.
create or replace function isdate
( p_date_str in varchar2 )
return varchar2
is
return_value varchar2(5) := 'FALSE';
l_date date;
l_date_fmts sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll (
'DD-MON-YYYY'
, 'YYYY-MM-DD'
, 'DD-MM-YY'
-- etc
);
begin
for idx in 1..l_date_fmts.count() loop
begin
l_date := to_date(p_date_str, l_date_fmts)idx) );
return_value := 'TRUE';
exit;
exception
when others then
null;
end;
end loop;
return return_value;
end isdate;
/
Related
I'd like to create a function to retrieve valid years for each record. I've got columns valid_from and valid_to in table.
For example valid_from = 2018-04-30 and valid_to = 2020-06-30; function should result three values: 2018, 2019, 2020.
I created a type and a function but it isn't working at all ...
create or replace type t_years is table of varchar2(4);
create or replace function func_year(d_from date, d_to date) return t_years pipelined
is
v_date date := '21/12/31 00:00:00';
max_date date := '99/12/30 00:00:00';
begin
for x in 1..2 loop
if d_from <= add_months(v_date,-1*12) AND (add_months(d_to,-1*12) >= v_date OR add_months(d_to,-1*12) = max_date) then return '2020';
elsif d_from <= add_months(v_date,-2*12) AND (add_months(d_to,-2*12) >= v_date OR add_months(d_to,-2*12) = max_date) then return '2019';
end if;
pipe row(x);
end loop;
return;
end;
Here's how:
SQL> CREATE OR REPLACE TYPE t_years IS TABLE OF VARCHAR2 (4);
2 /
Type created.
SQL> CREATE OR REPLACE FUNCTION func_year (d_from IN DATE, d_to IN DATE)
2 RETURN t_years
3 PIPELINED
4 IS
5 BEGIN
6 FOR i IN EXTRACT (YEAR FROM d_from) .. EXTRACT (YEAR FROM d_to)
7 LOOP
8 PIPE ROW (i);
9 END LOOP;
10
11 RETURN;
12 END;
13 /
Function created.
SQL> SELECT func_year (DATE '2019-01-01', DATE '2021-01-01') FROM DUAL;
FUNC_YEAR(DATE'2019-01-01',DATE'2021-01-01')
----------------------------------------------------------------------------------------------------
T_YEARS('2019', '2020', '2021')
SQL>
Or
SQL> SELECT * FROM TABLE (func_year (DATE '2019-01-01', DATE '2021-01-01'));
COLU
----
2019
2020
2021
SQL>
I have this procedure that goes PROCEDURE(monthday varchar2). It receives an varchar2 that represents the month and date concatenated always with the format of MMDD. I then want to create a DATE type variable that uses this month and day, and the year being the current year.
Like: desired_date DATE;
desired_date = ?
I'm using Oracle SQL Developer.
EXTRACT the year from SYSDATE and then combine using string concatenation with your input and use TO_DATE to convert to a date:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
END;
/
then:
DECLARE
dt DATE;
BEGIN
test( '0101', dt );
DBMS_OUTPUT.PUT_LINE( '0101: ' || dt );
test( '1231', dt );
DBMS_OUTPUT.PUT_LINE( '1231: ' || dt );
BEGIN
test( '9876', dt );
DBMS_OUTPUT.PUT_LINE( '9876: ' || dt );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( '9876: ' || SQLERRM );
END;
END;
/
outputs:
0101: 2019-01-01 00:00:00
1231: 2019-12-31 00:00:00
9876: ORA-01843: not a valid month
db<>fiddle here
If you want to return NULL for invalid inputs then:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
EXCEPTION
WHEN OTHERS THEN
-- In general don't catch OTHERS but in this case the only exceptions
-- are going to be from TO_DATE
desired_date := NULL;
END;
/
db<>fiddle here
Update:
You could simplify the code (as suggested by Aleksej and William Robertson) by not specifying the year value in TO_DATE and use the default which appears to be the current year; however this behaviour is not, obviously, documented in any Oracle documentation pages so I would also include inline documentation within the function so future developers reviewing your function know that you are deliberately using this behaviour:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
-- Assumes that TO_DATE will, when not specified, default the year to the
-- current year and the time to midnight.
desired_date := TO_DATE( monthday, 'MMDD' );
END;
/
This could be a way
desired_date := to_date(monthday, 'MMDD');
According to this old post in AskTom, to_date should use the current year, if not given:
default year = current year
default month = current month
default day = 1
default hour = 0
default minute = 0
default second = 0
ops$tkyte%ORA10GR2> select sysdate, to_date( ' ', ' ' ) from dual;
SYSDATE TO_DATE('','')
-------------------- --------------------
17-aug-2012 13:41:06 01-aug-2012 00:00:00
Still unable to find this information in Oracle Docs
I want to bring a string which contains a date to a single format date.
EX:
13-06-2012 to 13-JUN-12
13/06/2012 to 13-JUN-12
13-JUN-2012 to
13-JUN-12
13/jun-2012 to 13-JUN-12
...
I tried to delete all special characters and after that use a function to transform that string into a single format of date. My function return more exceptions, I don't know why...
The function:
CREATE OR REPLACE FUNCTION normalize_date (data_in IN VARCHAR2)
RETURN DATE
IS
tmp_month VARCHAR2 (3);
tmp_day VARCHAR2 (2);
tmp_year VARCHAR2 (4);
TMP_YEAR_NUMBER NUMBER;
result DATE;
BEGIN
tmp_day := SUBSTR (data_in, 1, 2);
tmp_year := SUBSTR (data_in, -4);
--if(REGEXP_LIKE(SUBSTR(data_in,3,2), '[:alpha:]')) then
if(SUBSTR(data_in,3,1) in ('a','j','i','f','m','s','o','n','d','A','J','I','F','M','S','O','N','D')) then
tmp_month := UPPER(SUBSTR (data_in, 3, 3));
else
tmp_month := SUBSTR (data_in, 3, 2);
end if;
DBMS_OUTPUT.put_line (tmp_year);
TMP_YEAR_NUMBER := TO_NUMBER (tmp_year);
IF (tmp_month = 'JAN')
THEN
tmp_month := '01';
END IF;
IF (tmp_month = 'FEB')
THEN
tmp_month := '02';
END IF;
IF (tmp_month = 'MAR')
THEN
tmp_month := '03';
END IF;
IF (tmp_month = 'APR')
THEN
tmp_month := '04';
END IF;
IF (tmp_month = 'MAY')
THEN
tmp_month := '05';
END IF;
IF (tmp_month = 'JUN')
THEN
tmp_month := '06';
END IF;
IF (tmp_month = 'JUL')
THEN
tmp_month := '07';
END IF;
IF (tmp_month = 'AUG')
THEN
tmp_month := '08';
END IF;
IF (tmp_month = 'SEP')
THEN
tmp_month := '09';
END IF;
IF (tmp_month = 'OCT')
THEN
tmp_month := '10';
END IF;
IF (tmp_month = 'NOV')
THEN
tmp_month := '11';
END IF;
IF (tmp_month = 'DEC')
THEN
tmp_month := '12';
END IF;
-- dbms_output.put_line(tmp_day || '~'||tmp_year || '~' ||tmp_month);
IF (LENGTH (tmp_day || tmp_year || tmp_month) <> 8)
THEN
result := TO_DATE ('31122999', 'DDMMYYYY');
RETURN result;
END IF;
-- dbms_output.put_line('before end');
result:=TO_DATE (tmp_day || tmp_month ||tmp_year , 'DDMMYYYY');
-- dbms_output.put_line('date result: '|| result);
RETURN result;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
NULL;
WHEN OTHERS
THEN
result := TO_DATE ('3012299', 'DDMMYYYY');
RETURN result;
RAISE;
END normalize_date;
Usage
SELECT customer_no,
str_data_expirare,
normalize_date (str_data_expirare_trim) AS data_expirare_buletin
FROM (SELECT customer_no,
str_data_expirare,
REGEXP_REPLACE (str_data_expirare, '[^a-zA-Z0-9]+', '')
AS str_data_expirare_trim
FROM (SELECT Q1.set_act_id_1,
Q1.customer_no,
NVL (SUBSTR (set_act_id_1,
INSTR (set_act_id_1,
'+',
1,
5)
+ 1,
LENGTH (set_act_id_1)),
'NULL')
AS str_data_expirare
FROM STAGE_CORE.IFLEX_CUSTOMERS Q1
WHERE Q1.set_act_id_1 IS NOT NULL
)
);
If you have a sound idea of all the possible date formats it might be easier to use brute force:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MON-YYYY', 'DD-MON-YY', 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD'
, 'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY/MM/DD', 'DD/MM/YY', 'MM/DD/YY');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
Be aware that modern versions of Oracle are quite forgiving with date conversion. This function handled dates in formats which aren't in the list, with some interesting consequences:
SQL> select clean_date('20160817') from dual;
CLEAN_DAT
---------
17-AUG-16
SQL> select clean_date('160817') from dual;
CLEAN_DAT
---------
16-AUG-17
SQL>
Which demonstrates the limits of automated data cleansing in the face of lax data integrity rules. The wages of sin is corrupted data.
#AlexPoole raises the matter of using the 'RR' format. This element of the date mask was introduced as a Y2K kludge. It's rather depressing that we're still discussing it almost two decades into the new Millennium.
Anyway, the issue is this. If we cast this string '161225' to a date what century does it have? Well, 'yymmdd' will give 2016-12-15. Fair enough, but what about '991225'? How likely is that the date we really want is 2099-12-15? This is where the 'RR' format comes into play. Basically it defaults the century: numbers 00-49 default to 20, 50-99 default to 19. This window was determined by the Y2K issue: in 2000 it was more likely that '98 referred to the recent past than the near future, and similar logic applied to '02. Hence the halfway point of 1950. Note this is a fixed point not a sliding window. As we move further from the year 2000 the less useful that pivot point becomes. Find out more.
Anyway, the key point is that 'RRRR' does not play nicely with other date formats: to_date('501212', 'rrrrmmdd') hurlsora-01843: not a valid month. So, use'RR'and test for it before using'YYYY'`. So my revised function (with some tidying up) looks like this:
create or replace function clean_date
( p_date_str in varchar2)
return date
is
l_dt_fmt_nt sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll
('DD-MM-RR', 'MM-DD-RR', 'RR-MM-DD', 'RR-DD-MM'
, 'DD-MM-YYYY', 'MM-DD-YYYY', 'YYYY-MM-DD', 'YYYY-DD-MM');
return_value date;
begin
for idx in l_dt_fmt_nt.first()..l_dt_fmt_nt.last()
loop
begin
return_value := to_date(p_date_str, l_dt_fmt_nt(idx));
exit;
exception
when others then null;
end;
end loop;
if return_value is null then
raise no_data_found;
end if;
return return_value;
exception
when no_data_found then
raise_application_error(-20000, p_date_str|| ' is unknown date format');
end clean_date;
/
The key point remains: there's a limit to how smart we can make this function when it comes to interpreting dates, so make sure you lead with the best fit. If you think most of your date strings fit day-month-year put that first; you will still get some wrong casts but less that if you lead with year-month-day.
The String-to-Date Conversion Rules allow additional formatting rules (without any other modifiers being applied). (Also see this question) So:
MM also matches MON and MONTH;
MON also matches MONTH (and vice versa);
YY also matches YYYY;
RR also matches RRRR; and
The punctuation is optional.
Which means you can do:
CREATE OR REPLACE FUNCTION parse_Date_String(
in_string VARCHAR2
) RETURN DATE DETERMINISTIC
IS
BEGIN
BEGIN
RETURN TO_DATE( in_string, 'DD-MM-YY' );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
RETURN TO_DATE( in_string, 'MM-DD-YY' );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
BEGIN
RETURN TO_DATE( in_string, 'YY-MM-DD' );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
RETURN NULL;
END;
/
Query:
WITH dates ( value ) AS (
SELECT '010101' FROM DUAL UNION ALL
SELECT '02JAN01' FROM DUAL UNION ALL
SELECT '03JANUARY01' FROM DUAL UNION ALL
SELECT '04012001' FROM DUAL UNION ALL
SELECT '05JAN2001' FROM DUAL UNION ALL
SELECT '06JANUARY2001' FROM DUAL UNION ALL
SELECT 'JAN0701' FROM DUAL UNION ALL
SELECT 'JANUARY0801' FROM DUAL UNION ALL
SELECT 'JAN0901' FROM DUAL UNION ALL
SELECT 'JANUARY1001' FROM DUAL UNION ALL
SELECT '990111' FROM DUAL UNION ALL
SELECT '99JAN12' FROM DUAL UNION ALL
SELECT '99JANUARY13' FROM DUAL UNION ALL
SELECT '19990114' FROM DUAL UNION ALL
SELECT '2001-01-15' FROM DUAL UNION ALL
SELECT '2001JAN16' FROM DUAL UNION ALL
SELECT '2001JANUARY17' FROM DUAL UNION ALL
SELECT '20010118' FROM DUAL
)
SELECT value, parse_Date_String( value ) AS dt
FROM dates;
Output:
VALUE DT
------------- -------------------
010101 2001-01-01 00:00:00
02JAN01 2001-01-02 00:00:00
03JANUARY01 2001-01-03 00:00:00
04012001 2001-01-04 00:00:00
05JAN2001 2001-01-05 00:00:00
06JANUARY2001 2001-01-06 00:00:00
JAN0701 2001-01-07 00:00:00
JANUARY0801 2001-01-08 00:00:00
JAN092001 2001-01-09 00:00:00
JANUARY102001 2001-01-10 00:00:00
990111 2099-01-11 00:00:00
99JAN12 2099-01-12 00:00:00
99JANUARY13 2099-01-13 00:00:00
19990114 1999-01-14 00:00:00
2001-01-15 2001-01-15 00:00:00
2001JAN16 2001-01-16 00:00:00
2001JANUARY17 2001-01-17 00:00:00
20010118 0118-01-20 00:00:00
(Note: the date formats you are using are ambiguous, as the last example demonstrates. You can swap the order the formats are parsed in the function to get different results but if you have 010203 is it 01-FEB-2003, 02-JAN-2003, 03-FEB-2001 or even 01-FEB-0003?)
If you want it in the format DD-MON-YY (but why YY and not YYYY?) then just use:
TO_CHAR( parse_Date_String( value ), 'DD-MON-YY' )
If I have a date format DDMM in PL/SQL and I want to validate it. What is the correct way to do it?
DD is the day and MM is the moth.
For an example:
0208 - is a valid date
3209 - is not a valid date
0113 - is not a valid date.
You could write a function like this one, for instance:
create or replace function is_valid(p_val in varchar2)
return number
is
not_a_valid_day exception;
not_a_valid_month exception;
pragma exception_init(not_a_valid_day, -1847);
pragma exception_init(not_a_valid_month, -1843);
l_date date;
begin
l_date := to_date(p_val, 'ddmm');
return 1;
exception
when not_a_valid_day or not_a_valid_month
then return 0;
end;
SQL> with test_dates(dt) as(
2 select '0208' from dual union all
3 select '3209' from dual union all
4 select '0113' from dual
5 )
6 select dt, is_valid(dt) as valid
7 from test_dates
8 /
DT VALID
---- ----------
0208 1
3209 0
0113 0
to_date raises an exception if its input parameter is not a valid date. So you can do something like:
declare
x date;
begin
x := to_date('3210', 'DDMM'); -- Will raise ORA-1847
x := to_date('0113', 'DDMM'); -- Will raise ORA-1843
exception
when others then
if sqlcode in (-1843, -1847) then
dbms_output.put_line('Invalid Date!');
null;
else
raise;
end if;
end;
Name: Calc_Anniversary
Input: Pay_Date, Hire_Date, Termination_Date
Output: "Y" if is the anniversary of the employee's Hire_Date, "N" if it is not, and "T" if he has been terminated before his anniversary.
Description: Create local variables to hold the month and day of the employee's Date_of_Hire, Termination_Date, and of the processing date using the TO_CHAR function. First check to see if he was terminated before his anniversary. The anniversary could be on any day during the pay period, so there will be a loop to check all 14 days in the pay period to see if one was his anniversary.
CREATE OR replace FUNCTION Calc_anniversary(
incoming_anniversary_date IN VARCHAR2)
RETURN BOOLEAN
IS
hiredate VARCHAR2(20);
terminationdate VARCHAR(20);
employeeid VARCHAR2(38);
paydate NUMBER := 0;
BEGIN
SELECT Count(arndt_raw_time_sheet_data.pay_date)
INTO paydate
FROM arndt_raw_time_sheet_data
WHERE paydate = incoming_anniversary_date;
WHILE paydate <= 14 LOOP
SELECT To_char(employee_id, '999'),
To_char(hire_date, 'DD-MON'),
To_char(termination_date, 'DD-MON')
INTO employeeid, hiredate, terminationdate
FROM employees,
time_sheet
WHERE employees.employee_id = time_sheet.employee_id
AND paydate = pay_date;
IF terminationdate > hiredate THEN
RETURN 'T';
ELSE
IF To_char(SYSDATE, 'DD-MON') = To_char(hiredate, 'DD-MON')THEN
RETURN 'Y';
ELSE
RETURN 'N';
END IF;
END IF;
paydate := paydate + 1;
END LOOP;
END;
Tables I am using
CREATE TABLE Employees ( EMPLOYEE_ID INTEGER,
FIRST_NAME VARCHAR2(15),
LAST_NAME VARCHAR2(25),
ADDRESS_LINE_ONE VARCHAR2(35),
ADDRESS_LINE_TWO VARCHAR2(35),
CITY VARCHAR2(28),
STATE CHAR(2),
ZIP_CODE CHAR(10),
COUNTY VARCHAR2(10),
EMAIL VARCHAR2(16),
PHONE_NUMBER VARCHAR2(12),
SOCIAL_SECURITY_NUMBER VARCHAR2(11),
HIRE_DATE DATE,
TERMINATION_DATE DATE,
DATE_OF_BIRTH DATE,
SPOUSE_ID INTEGER,
MARITAL_STATUS CHAR(1),
ALLOWANCES INTEGER,
PERSONAL_TIME_OFF FLOAT,
CONSTRAINT pk_employee_id PRIMARY KEY (EMPLOYEE_ID),
CONSTRAINT fk_spouse_id FOREIGN KEY (SPOUSE_ID) REFERENCES EMPLOYEES (EMPLOYEE_ID))
/
CREATE TABLE Arndt_Raw_Time_Sheet_data ( EMPLOYEE_ID INTEGER,
PAY_DATE DATE,
HOURS_WORKED FLOAT,
SALES_AMOUNT FLOAT,
CONSTRAINT pk_employee_id_pay_date_time PRIMARY KEY (EMPLOYEE_ID, PAY_DATE),
CONSTRAINT fk_employee_id_time FOREIGN KEY (EMPLOYEE_ID) REFERENCES EMPLOYEES (EMployee_ID));
error FUNCTION Calc_Anniversary compiled
Warning: execution completed with warning
Functions have to return something. It is normal for the RETURN statement to be the last statement in a function.
You have chosen not to do this, and that's why you're getting an error. All your RETURN statements are embedded in conditional branches, so if your logic never executes the loop you will never execute a RETURN.
Your loop logic is confused. You are populating your paydate as a count (so why is it called ""paydate"?) but your initialisation query compares 'paydate' to your parameter incoming_anniversary_date which is a date. Perhaps you meant to compare it to the tabel column pay_date? So who knows what your code is actually doing?
Anyway, the most important thing is to introduce some best practice into your function: you need to populate a variable and restrict yourself to just the one RETURN statement.
return_value char(1);
BEGIN
return_value := 'X';
....
WHILE paydate <= 14 LOOP
....
IF terminationdate > hiredate THEN
return_value := 'T';
ELSE
IF To_char(SYSDATE, 'DD-MON') = To_char(hiredate, 'DD-MON')THEN
return_value := 'Y';
ELSE
return_value := 'N';
END IF;
END IF;
...
END LOOP;
RETURN return_value;
END;
Also, this is wrong:
IF terminationdate > hiredate THEN
You converted those dates to strings, which means that '23-JAN' > '22-DEC'. This is probably not the result you intend.
Oh, and rename your variable paydate to something a bit less confusing, like l_count.
create or replace
FUNCTION Calc_Anniversary(employee IN VARCHAR2)
RETURN VARCHAR2
IS
counter INTEGER;
term_date VARCHAR2(15);
h_date VARCHAR2(15);
p_date DATE;
BEGIN
SELECT TO_CHAR(hire_date, 'DD-MON'), TO_CHAR(termination_date, 'DD-MON'), pay_date INTO h_date, term_date, p_date
FROM employees e, diaz_raw_time_sheet_data d
WHERE e.employee_id = d.employee_id
AND e.employee_id = employee;
FOR counter IN 0 .. 14 LOOP
IF term_date > h_date THEN
RETURN 'T';
ELSIF TO_CHAR(p_date,'DD-MON') = h_date THEN
RETURN 'Y';
END IF;
p_date := p_date - 1;
END LOOP;
IF h_date <> TO_CHAR(p_date, 'DD-MON') THEN
RETURN 'N';
END IF;
END;