工具 : PLSQL Developer
要求:
给定一个时间(date 类型, yyyy-MM-dd HH:mm:ss),求过了n个星期后,此时间的下一个月的时间.
CREATE OR REPLACE FUNCTION FN_RECURSIVE_DATE(EXEC_TIME IN DATE, -- 执行时间
V_CYCLE IN NUMBER, -- n周
OUT_DATE OUT DATE) -- 执行时间执行n周后的下一个月时间
RETURN DATE AS
/*按周期赠送,按周的递归函数,找到配置的执行时间的下一个月*/
V_EXEC_DAY NUMBER(2); -- 执行时间EXEC_TIME中的天
V_EXEC_MONTH NUMBER(2); -- 执行时间EXEC_TIME中的月份
V_EXEC_YEAR VARCHAR2(4); -- 执行时间EXEC_TIME中的年份
V_DAYS NUMBER(4); -- 总天数
V_STR_EXEC_TIME VARCHAR2(20); -- 执行时间EXEC_TIME的字符串表示方式
V_SPACE_INDEX NUMBER(2); -- 执行时间EXEC_TIME中空格所在索引
V_OTHERS_TIME VARCHAR2(20); -- EXEC_TIME的格式' HH:mm:ss'
V_MAX_DAY NUMBER(2); -- 某个月的最大天数
V_CUR_YEAR VARCHAR2(4); -- 当前年份
V_CUR_MONTH NUMBER(2); -- 当前月份
V_N_EXEC_YEAR NUMBER(4); -- V_EXEC_YEAR 的number格式
V_NEXT_MONTH NUMBER(2); -- 下一月
V_N_CUR_YEAR NUMBER(4); -- v_cur_year 的number格式
BEGIN
-- 取出execTime的day
V_EXEC_DAY := FN_GET_DAY_OF_DATE(EXEC_TIME, V_EXEC_DAY);
-- 取出执行时间execTime中的月份
V_EXEC_MONTH := F_GET_MONTH(EXEC_TIME, V_EXEC_MONTH);
-- execTime 的年份
V_EXEC_YEAR := FN_GET_YEAR_OF_DATE(EXEC_TIME, V_EXEC_YEAR);
-- n周的天数+执行时间中的day
V_DAYS := V_EXEC_DAY + V_CYCLE * 7;
-- 将exec_time转成string格式
V_STR_EXEC_TIME := TO_CHAR(EXEC_TIME, 'yyyy-mm-dd hh24:mi:ss');
-- 找出exec_time中空格的位置
V_SPACE_INDEX := INSTR(V_STR_EXEC_TIME, ' ', 1, 1);
-- ' HH:mm:ss'
V_OTHERS_TIME := SUBSTR(V_STR_EXEC_TIME, V_SPACE_INDEX);
-- 某个月的最大天数
V_MAX_DAY := FN_MAXDAYOFMONTH(V_EXEC_MONTH, V_EXEC_YEAR, V_MAX_DAY);
IF V_DAYS > V_MAX_DAY THEN
/* 如果总天数大于该月的最大天数 */
V_EXEC_MONTH := V_EXEC_MONTH + 1;
IF V_EXEC_MONTH > 12 THEN
V_EXEC_MONTH := 1;
V_EXEC_YEAR := TO_CHAR(TO_NUMBER(V_EXEC_YEAR) + 1);
END IF;
-- end of IF V_EXEC_MONTH > 12
V_EXEC_DAY := V_DAYS - V_MAX_DAY;
-- 获取当前月份
V_CUR_MONTH := F_GET_MONTH(SYSDATE, V_CUR_MONTH);
-- 执行年份的number格式
V_N_EXEC_YEAR := TO_NUMBER(V_EXEC_YEAR);
V_NEXT_MONTH := V_CUR_MONTH + 1;
IF V_NEXT_MONTH > 12 THEN
V_NEXT_MONTH := 1;
V_N_EXEC_YEAR := V_N_EXEC_YEAR + 1;
END IF;
-- end of IF v_next_month > 12
-- 获取当前年份
V_CUR_YEAR := FN_GET_YEAR_OF_DATE(SYSDATE, V_CUR_YEAR);
V_N_CUR_YEAR := TO_NUMBER(V_CUR_YEAR);
IF (V_N_CUR_YEAR = V_N_EXEC_YEAR OR V_N_CUR_YEAR + 1 = V_N_EXEC_YEAR) AND
V_NEXT_MONTH = V_EXEC_MONTH THEN
/* 是下一个月 */
V_STR_EXEC_TIME := V_EXEC_YEAR || '-' || V_EXEC_MONTH || '-' ||
V_EXEC_DAY || V_OTHERS_TIME;
OUT_DATE := TO_DATE(V_STR_EXEC_TIME, 'yyyy-mm-dd hh24:mi:ss');
RETURN OUT_DATE;
END IF;
ELSE
/* 没到下一个月 */
-- 这里的day写的是 V_DAYS (相加后的总天数) , 不然会出现死循环
V_STR_EXEC_TIME := V_EXEC_YEAR || '-' || V_EXEC_MONTH || '-' ||
V_DAYS || V_OTHERS_TIME;
OUT_DATE := TO_DATE(V_STR_EXEC_TIME, 'yyyy-mm-dd hh24:mi:ss');
-- 递归调用
RETURN FN_RECURSIVE_DATE(OUT_DATE, V_CYCLE, OUT_DATE);
END IF;
-- end of if V_DAYS > V_MAX_DAY
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('execute FN_RECURSIVE_DATE fail!' || SQLERRM);
END;
/
执行:
在工具 PLSQL Developer 的Command Window 中输入以下的代码:
VAR v_date DATE;
EXEC :v_date := FN_RECURSIVE_DATE(SYSDATE,2,:v_date);
执行结果如下所示: