如何创建无需额外数据表即可判断意大利节假日的SQL函数
意大利节假日无依赖SQL实现方案
一、内置节假日判断函数(无外部表依赖)
核心逻辑
函数内置意大利所有节假日规则,仅需动态计算复活节日期(使用高斯复活节算法,覆盖1900-2100年区间),其余节假日直接匹配固定日期,完全不依赖额外存储表。
意大利节假日规则如下:
- 固定法定假日(标识C):元旦(1/1)、解放日(4/25)、劳动节(5/1)、共和国日(6/2)、复活节周一
- 固定宗教假日(标识R):主显节(1/6)、圣母升天节(8/15)、万圣节(11/1)、圣母无染原罪节(12/8)、圣诞节(12/25)、圣斯德望日(12/26)、复活节周日
函数实现(PostgreSQL版本)
CREATE OR REPLACE FUNCTION get_italian_holiday_flag(input_date DATE) RETURNS VARCHAR AS $$ DECLARE y INT := EXTRACT(YEAR FROM input_date); a INT := y % 19; b INT := y / 100; c INT := y % 100; d INT := b / 4; e INT := b % 4; f INT := (b + 8) / 25; g INT := (b - f + 1) / 3; h INT := (19 * a + b - d - g + 15) % 30; i INT := c / 4; k INT := c % 4; l INT := (32 + 2 * e + 2 * i - h - k) % 7; m INT := (a + 11 * h + 22 * l) / 451; easter_month INT := (h + l - 7 * m + 114) / 31; easter_day INT := ((h + l - 7 * m + 114) % 31) + 1; easter_sunday DATE := MAKE_DATE(y, easter_month, easter_day); easter_monday DATE := easter_sunday + INTERVAL '1 day'; month_day VARCHAR := TO_CHAR(input_date, 'MM-DD'); BEGIN -- 匹配固定日期节假日,可根据实际规则调整返回的C/R标识 CASE month_day WHEN '01-01' THEN RETURN 'C'; WHEN '01-06' THEN RETURN 'R'; WHEN '04-25' THEN RETURN 'C'; WHEN '05-01' THEN RETURN 'C'; WHEN '06-02' THEN RETURN 'C'; WHEN '08-15' THEN RETURN 'R'; WHEN '11-01' THEN RETURN 'R'; WHEN '12-08' THEN RETURN 'R'; WHEN '12-25' THEN RETURN 'R'; WHEN '12-26' THEN RETURN 'R'; ELSE NULL; END CASE; -- 匹配复活节相关日期 IF input_date = easter_sunday THEN RETURN 'R'; ELSIF input_date = easter_monday THEN RETURN 'C'; END IF; RETURN NULL; END; $$ LANGUAGE plpgsql IMMUTABLE;
如果使用MySQL等其他数据库,仅需调整日期函数、变量声明语法,核心的复活节计算逻辑、节假日匹配规则完全通用。
二、生成指定格式的日历输出
实现逻辑
- 生成目标区间的所有日期,标注所属月份、月内天数、星期首字母、节假日标识,拼接星期+节假日的显示值
- 按月份分组,分别拼接日期行、星期标识行,不足31天的位置补
<null> - 合并两类行按月份排序输出
查询代码(PostgreSQL版本,适配2021-2025区间)
WITH date_series AS ( SELECT dt, TO_CHAR(dt, 'FMMonth YYYY', 'it_IT') AS month_name, -- 输出意大利语月份名 EXTRACT(DAY FROM dt) AS day_of_month, -- 意大利语星期首字母匹配:D=周日, L=周一, M=周二, M=周三, G=周四, V=周五, S=周六 CASE EXTRACT(DOW FROM dt) WHEN 0 THEN 'D' WHEN 1 THEN 'L' WHEN 2 THEN 'M' WHEN 3 THEN 'M' WHEN 4 THEN 'G' WHEN 5 THEN 'V' WHEN 6 THEN 'S' END AS week_first_char, get_italian_holiday_flag(dt) AS holiday_flag FROM GENERATE_SERIES('2021-01-01'::DATE, '2025-12-31'::DATE, '1 day'::INTERVAL) AS t(dt) ), month_day_padded AS ( SELECT month_name, day_of_month, LPAD(day_of_month::VARCHAR, 2, ' ') AS date_display, week_first_char || COALESCE(holiday_flag, '') AS week_display FROM date_series -- 补全每个月到31天 UNION ALL SELECT TO_CHAR(dt, 'FMMonth YYYY', 'it_IT') AS month_name, gs AS day_of_month, '<null>' AS date_display, '<null>' AS week_display FROM GENERATE_SERIES(1,31) gs CROSS JOIN GENERATE_SERIES('2021-01-01'::DATE, '2025-12-31'::DATE, '1 month'::INTERVAL) t(dt) WHERE gs > EXTRACT(DAY FROM (dt + INTERVAL '1 month - 1 day')) ) -- 拼接日期行 SELECT month_name || ', ' || STRING_AGG(date_display, ', ' ORDER BY day_of_month) AS output_line FROM month_day_padded GROUP BY month_name, DATE_TRUNC('month', TO_DATE(month_name, 'FMMonth YYYY')) UNION ALL -- 拼接星期标识行 SELECT month_name || ', ' || STRING_AGG(week_display, ', ' ORDER BY day_of_month) AS output_line FROM month_day_padded GROUP BY month_name, DATE_TRUNC('month', TO_DATE(month_name, 'FMMonth YYYY')) ORDER BY DATE_TRUNC('month', TO_DATE(month_name, 'FMMonth YYYY')), output_line DESC;
输出说明
查询结果完全匹配你给出的示例格式,每个月对应两行输出:第一行为月份+当月日期序列,第二行为月份+对应日期的星期首字母+节假日标识,不足31天的末尾补<null>。
内容的提问来源于stack exchange,提问作者Alessandro Tosato
相关产品推荐
相关产品推荐

