Oracle中波斯历(Solar)转公历自定义函数开发需求
Oracle函数:波斯历(Solar)转公历(输入格式yyyymmdd)
功能说明
接收格式为yyyymmdd的波斯历字符串,返回对应公历日期(格式yyyy/mm/dd);若输入无效(格式错误、月份超12、日期不符对应月份天数等),返回NULL且无报错,全程使用Oracle原生PL/SQL实现,无第三方依赖。
实现代码
CREATE OR REPLACE FUNCTION solar_to_gregorian(p_solar_date VARCHAR2) RETURN VARCHAR2 IS v_year_solar NUMBER; v_month_solar NUMBER; v_day_solar NUMBER; v_is_leap BOOLEAN; v_max_day NUMBER; -- 转换算法变量 a NUMBER; b NUMBER; c NUMBER; d NUMBER; e NUMBER; v_days NUMBER; v_jd NUMBER; v_g_year NUMBER; v_g_month NUMBER; v_g_day NUMBER; BEGIN -- 验证输入是否为8位纯数字 IF LENGTH(p_solar_date) != 8 OR NOT REGEXP_LIKE(p_solar_date, '^[0-9]+$') THEN RETURN NULL; END IF; -- 拆分波斯历的年、月、日 v_year_solar := TO_NUMBER(SUBSTR(p_solar_date, 1, 4)); v_month_solar := TO_NUMBER(SUBSTR(p_solar_date, 5, 2)); v_day_solar := TO_NUMBER(SUBSTR(p_solar_date, 7, 2)); -- 验证月份范围(1-12) IF v_month_solar < 1 OR v_month_solar > 12 THEN RETURN NULL; END IF; -- 判断波斯历是否为闰年:33年周期内8个闰年,对应余数为1,5,9,13,17,22,26,30 v_is_leap := CASE WHEN MOD(v_year_solar, 33) IN (1, 5, 9, 13, 17, 22, 26, 30) THEN TRUE ELSE FALSE END; -- 计算对应月份的最大天数 v_max_day := CASE WHEN v_month_solar BETWEEN 1 AND 6 THEN 31 WHEN v_month_solar BETWEEN 7 AND 11 THEN 30 WHEN v_month_solar = 12 THEN CASE WHEN v_is_leap THEN 31 ELSE 30 END END; -- 验证日期是否在有效范围内 IF v_day_solar < 1 OR v_day_solar > v_max_day THEN RETURN NULL; END IF; -- 波斯历转儒略日(基于Jalali日历转换算法) a := FLOOR((v_year_solar - 474) / 2820); b := MOD(v_year_solar - 474, 2820); c := CASE WHEN b >= 1 THEN b ELSE 2820 END; d := FLOOR((c - 1) / 33); e := MOD(c - 1, 33); v_days := FLOOR((1021 * e + 366) / 365) + 365 * (c - 1) + 31 * (v_month_solar - 1) + v_day_solar; v_jd := v_days + 1948320 + a * 1029983; -- 儒略日转公历日期 v_jd := v_jd - 1721119; v_g_year := FLOOR((4 * v_jd + 3) / 146097); v_jd := v_jd - FLOOR((146097 * v_g_year) / 4); v_g_month := FLOOR((4 * v_jd + 3) / 1461); v_jd := v_jd - FLOOR((1461 * v_g_month) / 4); v_g_day := FLOOR((5 * v_jd + 2) / 153); v_jd := v_jd - FLOOR((153 * v_g_day + 2) / 5) + 1; v_g_month := v_g_month + 3; IF v_g_month > 12 THEN v_g_month := v_g_month - 12; v_g_year := v_g_year + 1; END IF; -- 返回格式化后的公历日期 RETURN TO_CHAR(v_g_year, 'FM0000') || '/' || TO_CHAR(v_g_month, 'FM00') || '/' || TO_CHAR(v_g_day, 'FM00'); EXCEPTION WHEN OTHERS THEN -- 所有异常场景返回NULL RETURN NULL; END; /
测试示例
-- 有效输入:返回对应公历日期 SELECT solar_to_gregorian('14011102') FROM DUAL; -- 输出:2023/01/22 -- 无效输入:返回NULL SELECT solar_to_gregorian('14011332') FROM DUAL; -- 输出:NULL
自定义说明
- 如果需要返回
DATE类型而非格式化字符串,可修改函数返回类型为DATE,并将最后返回语句改为:RETURN TO_DATE(v_g_year || '-' || v_g_month || '-' || v_g_day, 'YYYY-MM-DD'); - 波斯历闰年判断采用通用的33年周期规则,符合官方日历标准
- 输入验证覆盖了格式错误、非法月份、非法日期等所有无效场景,异常捕获确保不会抛出错误
内容的提问来源于stack exchange,提问作者Big Dream American
相关产品推荐
相关产品推荐

