Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
CREATE GLOBAL TEMPORARY TABLE tst_DATE_LIST
(
DT_TYPE VARCHAR2(25) NULL,
DT_OUT VARCHAR2(23) NULL
)
ON COMMIT DELETE ROWS
ORGANIZATION HEAP
/
CREATE OR REPLACE PROCEDURE tstSP_DATE_TEXT
(
-- =============================================
-- declare input and OUTPUT parametes
-- =============================================
pdtInDate IN DATE,
pvcOutDate OUT VARCHAR2
)
AS
BEGIN
DECLARE
lchMonth CHAR(2);
lchDay CHAR(2);
lchYear CHAR(4);
lchHour CHAR(2);
lchMin CHAR(2);
lchSec CHAR(2);
lchMilSec CHAR(3);
-- =============================================
-- Calculate text portion of dates and place in variables
-- =============================================
BEGIN
lchMonth := to_char(pdtInDate,'MM');
lchDay := to_char(pdtInDate,'DD');
lchYear := to_char(pdtInDate,'YYYY');
lchHour := to_char(pdtInDate,'HH24');
lchMin := to_char(pdtInDate,'MI');
lchSec := to_char(pdtInDate,'SS');
lchMilSec := to_char(pdtInDate,'MS');
-- zero fil
IF to_number(lchMonth) < 10 THEN
lchMonth := '0' + lchMonth;
END IF;
-- zero fill
IF to_number(lchDay) < 10 THEN
lchDay := '0' + lchDay;
END IF;
-- zero fill
IF to_number(lchHour) < 10 THEN
lchHour := '0' + lchHour;
END IF;
-- zero fill
IF to_number(lchMin) < 10 THEN
lchMin := '0' + lchMin;
END IF;
--zero fill
IF to_number(lchSec) < 10 THEN
lchSec := '0' + lchSec;
END IF;
-- zero fill
IF to_number(lchMilSec) < 10 THEN
lchMilSec := '00' + lchMilSec;
END IF;
pvcOutDate := lchMonth + '/' + lchDay || '/' + lchYear + ' ' + lchHour + ':' + lchMin + ':' + lchSec + '.' + lchMilSec;
END;
END tstSP_DATE_TEXT;
/
CREATE OR REPLACE FUNCTION tstSP_RUNDATE_LIST
(
-- =============================================
-- declare input parametes
-- =============================================
pRESULT_CURSOR OUT SYS_REFCURSOR,
pdtStartDate IN DATE := to_date('01/01/1753','mm/dd/yyyy'),
pvcWeekday IN VARCHAR2 default null
)
RETURN NUMBER
AS
BEGIN
DECLARE
ldtEndMonthp DATE;
ldtEndMontht DATE;
ldtEndMonthn DATE;
ldtBegMonth DATE;
lnDOW INT;
lnDOWThis INT;
ldtSpecDay DATE;
lnDOWAbs INT;
lnDOWEnd INT;
ldtBatchDate DATE;
ldtBeforeStart DATE;
lvcTextDate VARCHAR2(23);
ldtDateCalc DATE;
ldtTempDate DATE;
lvcWeekday VARCHAR2(20);
BEGIN
--If the current date has not been input then get the current date
IF (pdtStartDate = to_date('01/01/1753','mm/dd/yyyy')) THEN
ldtTempDate := SYSDATE ;
END IF;
--If the weekday has not bee input then get the weekday of @pdtStartDate
--expressed as text date.
lvcWeekday := pvcWeekday;
IF (lvcWeekday || 'b' = 'b') THEN
lvcWeekday :=
CASE to_char(ldtTempDate, 'dw')
WHEN 1 THEN 'SUNDAY'
WHEN 2 THEN 'MONDAY'
WHEN 3 THEN 'TUESDAY'
WHEN 4 THEN 'WEDNESDAY'
WHEN 5 THEN 'THURSDAY'
WHEN 6 THEN 'FRIDAY'
WHEN 7 THEN 'SATURDAY'
END;
END IF;
lnDOW :=
CASE UPPER(pvcWeekday)
WHEN 'SUNDAY' THEN 1
WHEN 'MONDAY' THEN 2
WHEN 'TUESDAY' THEN 3
WHEN 'WEDNESDAY' THEN 4
WHEN 'THURSDAY' THEN 5
WHEN 'FRIDAY' THEN 6
WHEN 'SATURDAY' THEN 7
WHEN 'SUN' THEN 1
WHEN 'MON' THEN 2
WHEN 'TUES' THEN 3
WHEN 'WED' THEN 4
WHEN 'THUR' THEN 5
WHEN 'FRI' THEN 6
WHEN 'SAT' THEN 7
ELSE 0
END;
IF lnDOW = 0 THEN
RETURN 17001; --@@ERROR ***FIX***
END IF;
--Each date calculation has 3 parts, Calculate the date, Convert it to text, and insert
--it into the temporary table.
--
-- ########## Store today and input dates in temporary table
ldtDateCalc := pdtStartDate;
tstSP_DATE_TEXT (ldtDateCalc, lvcTextDate);
INSERT INTO tst_date_list VALUES ('CURRENT_DATE', lvcTextDate);
tstSP_DATE_TEXT (pdtStartDate, lvcTextDate);
INSERT INTO tst_date_list VALUES ('INPUT_DATE', lvcTextDate);
-- ############ Previous month end caculation inserted into temporary table
ldtEndMonthp := DATEADD('DD', - to_char(ldtTempDate, 'DD'), ldtTempDate);
tstSP_DATE_TEXT (ldtEndMonthp, lvcTextDate);
INSERT INTO tst_date_list VALUES ('LAST_PREV_MONTH', lvcTextDate);
-- ########### This month end date caculation inserted into temporary table
ldtEndMontht := DATEADD('DD', - to_char(DATEADD('MM', 1, ldtTempDate),'DD'), DATEADD('MM', 1, ldtTempDate));
tstSP_DATE_TEXT (ldtEndMontht, lvcTextDate);
INSERT INTO tst_date_list VALUES ('LAST_CUR_MONTH', lvcTextDate);
-- list temporary table send data to OUTPUT #####################################
OPEN pRESULT_CURSOR FOR
SELECT DT_TYPE, DT_OUT from tst_date_list
ORDER BY DT_TYPE;
RETURN 0;
END;
END;
-- Stored procedure ends here
/
...IF (lvcWeekday || 'b' = 'b')...
IF lvcWeekday [B][I]IS NULL[/I][/B]...
ldtEndMonthp := DATEADD('DD', - to_char(ldtTempDate, 'DD'), ldtTempDate);
When you create user-defined PL/SQL code objects (e.g. user-defined stored PROCEDURES, FUNCTIONS, PACKAGES, et cetera), the "IS/AS" syntax takes the place of PL/SQL's anonymous blocks' "DECLARE" header. By using the standard "IS/AS" construct, it eliminates the contrived two BEGINS and two END statements that you had in your code posting, above.[/li][/ul]Davism said:I noted the function itself has a 2 BEGINS and 2 ENDS. I'm not entirely sure. The second BEGIN is after the declare and if I take that out I get a failure.
CREATE OR REPLACE FUNCTION tstSP_RUNDATE_LIST (pdtStartDate DATE default null)
RETURN sys_refcursor
AS
ldtTempDate DATE;
pRESULT_CURSOR sys_refcursor;
BEGIN
--If the current date has not been input then get the current date
ldtTempDate := pdtStartDate;
IF pdtStartDate IS NULL THEN
ldtTempDate := SYSDATE;
END IF;
-- ########## Store today and input dates in temporary table
INSERT INTO tst_date_list VALUES ('CURRENT_DATE', to_char(pdtStartDate,'MM/DD/YYYY hh24:mi:ss'));
INSERT INTO tst_date_list VALUES ('INPUT_DATE', to_char(pdtStartDate,'MM/DD/YYYY hh24:mi:ss'));
-- ############ Previous month end caculation inserted into temporary table
INSERT INTO tst_date_list VALUES ('LAST_PREV_MONTH'
, to_char(trunc(ldtTempDate,'MM')-(1/24/60/60),'MM/DD/YYYY hh24:mi:ss'));
-- ########### This month end date caculation inserted into temporary table
INSERT INTO tst_date_list VALUES ('LAST_CUR_MONTH'
, to_char(trunc(last_day(ldtTempDate))+1-(1/24/60/60),'mm/dd/yyyy hh24:mi:ss'));
-- list temporary table send data to OUTPUT #####################################
OPEN pRESULT_CURSOR FOR
SELECT DT_TYPE, DT_OUT from tst_date_list
ORDER BY DT_TYPE;
RETURN pResult_cursor;
END;
-- FUNCTION ends here
/
Function created.
set serveroutput on
declare
foo sys_refcursor;
y tst_DATE_LIST%rowtype;
begin
foo := tstSP_RUNDATE_LIST(sysdate);
loop
fetch foo into y;
exit when foo%notfound;
dbms_output.put_line(y.dt_type||', '||y.dt_out);
end loop;
end;
/
CURRENT_DATE, 12/05/2008 20:51:17
INPUT_DATE, 12/05/2008 20:51:17
LAST_CUR_MONTH, 12/31/2008 23:59:59
LAST_PREV_MONTH, 11/30/2008 23:59:59
PL/SQL procedure successfully completed.
INSERT INTO tst_date_list VALUES ('LAST_CUR_MONTH'
, to_char(trunc(last_day(ldtTempDate))+1-(1/24/60/60),'mm/dd/yyyy hh24:mi:ss'));
+1-(1/24/60/60)
col a heading "Current Time" format a20
col b heading "Last Day of|Current Month" format a20
col c heading "Midnight (morning)|Last Day of|Current Month" format a20
col d heading "First Day of|Next Month" format a20
col e heading "Last Second of|Current Month" format a20
select to_char(sysdate,'mm/dd/yyyy hh24:mi:ss') a
,to_char(last_day(sysdate),'mm/dd/yyyy hh24:mi:ss') b
,to_char(trunc(last_day(sysdate)),'mm/dd/yyyy hh24:mi:ss') c
,to_char(trunc(last_day(sysdate))+1,'mm/dd/yyyy hh24:mi:ss') d
,to_char(trunc(last_day(sysdate))+1-(1/24/60/60),'mm/dd/yyyy hh24:mi:ss') e
from dual;
Midnight (morning)
Last Day of Last Day of First Day of Last Second of
Current Time Current Month Current Month Next Month Current Month
-------------------- -------------------- -------------------- -------------------- --------------------
12/07/2008 17:54:29 12/31/2008 17:54:29 12/31/2008 00:00:00 01/01/2009 00:00:00 12/31/2008 23:59:59
Yes, kind of, depending upon your use/need for the LAST_DAY of the month. That's why I tried to clarify in my reply, above, what your use/need is for LAST_DAY...I said,Davism said:So you can use LAST_DAY of SYSDATE for the last day of the current month.
Notice in my code, above, that LAST_DAY(SYSDATE) does yield the last day of the current month, but it also yields on that last day, a TIME component that is based on the current time, not midnight on the morning of LAST_DAY or just before midnight at the end of LAST_DAY.Mufasa said:I'll presume that you want 23:59:59 on the last day of the month
set serveroutput on format wrap
declare
target_time date := to_date('31-DEC-2008 21:00:00','dd-MON-YYYY hh24:mi:ss');
now date := sysdate;
last_day_only date := last_day(sysdate);
last_day_last_second date := trunc(last_day(sysdate))+1-(1/24/60/60);
is_or_isnot varchar2(30);
begin
if target_time between now and last_day_only then
is_or_isnot := ' is between ';
else
is_or_isnot := ' is not between ';
end if;
dbms_output.put_line(
to_char(target_time,'mm/dd/yyyy hh24:mi:ss')||is_or_isnot
||to_char(now,'mm/dd/yyyy hh24:mi:ss')||' and '
||to_char(last_day_only,'mm/dd/yyyy hh24:mi:ss')||'.'
);
if target_time between now and last_day_last_second then
is_or_isnot := ' is between ';
else
is_or_isnot := ' is not between ';
end if;
dbms_output.put_line(
to_char(target_time,'mm/dd/yyyy hh24:mi:ss')||is_or_isnot||
to_char(now,'mm/dd/yyyy hh24:mi:ss')||' and '||
to_char(last_day_last_second,'mm/dd/yyyy hh24:mi:ss')||'.');
end;
/
12/31/2008 21:00:00 is not between 12/07/2008 17:55:41 and 12/31/2008 17:55:41.
12/31/2008 21:00:00 is between 12/07/2008 17:55:41 and 12/31/2008 23:59:59.
I coded these calculations in order to yield dates that are one second before midnight. Referring to my first code example of this reply:[ul][li]last_day(sysdate) -- yields the last day of the month for sysdate, but the TIME matches SYSDATE.Davism said:You are using last day in that. Why and what is the:getting at here? The +1 is for what? Then the -(1/24/60/60) is what?Code:+1-(1/24/60/60)
Yes, you can do that, but you will still need to adjust the TIME component to 23:59:59.Davism said:For the last day of the previous month can't you use the function ADD_MONTHS(SYSDATE,-1) and then get the LAST_DAY of that value so something like LAST_DAY(ADD_MONTHS(SYSDATE,-1))
All I know is that Oracle does throw an error indiating that it does not recognize "dateadd":Davism said:The DATEADD function was from SQL Server where that is used for DATE calculations. Obviously, I thought since it compiled in Oracle then that was value and those would be able to be used there as well. From what you mentioned, it seems as though that was a false assumption and there was no compile error on Oracle as a result. Why that is I do not know but it's very strange and I would've expected a compilation error as that function just is not there as a system function or a user-defined function. Do you have any idea?
select dateadd(sysdate) from dual;
*
ERROR at line 1:
ORA-00904: "DATEADD": invalid identifier
We either ROUND or TRUNC (truncate) time values, while specifying the ROUND/TRUNC magnitude (i.e., minute, hour, day, week, month, year, century):Davism said:I would use a DATEADD(DD,1, {previous last day of last month variable}) to give me the first of the current month.
How is that done in Oracle?
col a heading "Beginning of Minute" format a20
col b heading "Beginning of Hour" format a20
col c heading "Beginning of Day" format a20
col d heading "Beginning of Week" format a20
col e heading "Beginning of Month" format a20
col f heading "Beginning of Year" format a20
col g heading "Beginning of Century" format a20
select to_char(trunc(sysdate,'MI'),'mm/dd/yyyy hh24:mi:ss') a -- trunc to beg of minute
,to_char(round(sysdate,'HH'),'mm/dd/yyyy hh24:mi:ss') b -- trunc to beg of hour
,to_char(trunc(sysdate),'mm/dd/yyyy hh24:mi:ss') c -- trunc to beg of day
,to_char(trunc(sysdate,'W'),'mm/dd/yyyy hh24:mi:ss') d -- trunc to beg of week
,to_char(trunc(sysdate,'MM'),'mm/dd/yyyy hh24:mi:ss') e -- trunc to beg of month
,to_char(trunc(sysdate,'Y'),'mm/dd/yyyy hh24:mi:ss') f -- trunc to beg of year
,to_char(trunc(sysdate,'CC'),'mm/dd/yyyy hh24:mi:ss') g -- trunc to beg of Century
from dual;
Beginning of Minute Beginning of Hour Beginning of Day Beginning of Week
-------------------- -------------------- -------------------- --------------------
12/07/2008 17:56:00 12/07/2008 18:00:00 12/07/2008 00:00:00 12/01/2008 00:00:00
Beginning of Month Beginning of Year Beginning of Century
-------------------- -------------------- --------------------
12/01/2008 00:00:00 01/01/2008 00:00:00 01/01/2001 00:00:00
As you can see from the above code, TRUNC does not remove time references...it just alters the value to a specific boundary. Oracle DATE expressions always have a time component. Ignoring that feature can lead to significant logic errors.Davism said:TRUNC removes the time references.
...
if trunc(target_time) between trunc(now)
and trunc(last_day_only) then
...
Does this properly resolve your question(s)?Mufasa said:Ignoring that feature (TRUNC does not remove time references)...can lead to significant logic errors.
...to_date('12/02/2008','mm/dd/yyyy')...