You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Firebird 2.1存储过程中用DateDiff计算含闰月闰年的精确年龄

在Firebird 2.1存储过程中计算流逝时间(年/月/日)的正确实现

先说说你原有代码的问题

你原来的代码只尝试计算年数,用YEARDAY的方式存在明显漏洞:

  • YEARDAY是一年中的第几天,但闰年的2月29日在平年没有对应天数,会导致判断逻辑出错
  • 完全没覆盖月和日的精确计算,跨月、跨日的场景根本处理不了

完整的实现思路

因为Firebird 2.1没有直接返回年/月/日间隔的内置函数,咱们得手动分步计算,同时重点处理闰年、闰月的特殊情况:

  1. 先判断指定日期是否晚于当前日期,这种情况直接返回0年0月0日
  2. 计算年数:先取基础年差,再通过对比加年数后的日期和当前日期调整年数,重点处理闰年2月29日的场景
  3. 基于调整后的年数,计算剩余的月数,同样通过日期对比调整月数
  4. 最后计算剩余的天数

完整的存储过程代码

CREATE OR ALTER PROCEDURE CALCULATE_ELAPSED_TIME(
    CustomDateTime DATE
)
RETURNS(
    ElapsedYears INTEGER,
    ElapsedMonths INTEGER,
    ElapsedDays INTEGER
)
AS
DECLARE VARIABLE BaseDate DATE;
DECLARE VARIABLE TempDate DATE;
BEGIN
    -- 处理指定日期晚于当前日期的情况
    IF (CustomDateTime > CURRENT_DATE) THEN
    BEGIN
        ElapsedYears = 0;
        ElapsedMonths = 0;
        ElapsedDays = 0;
        SUSPEND;
    END;

    -- 计算年数
    ElapsedYears = DATEDIFF(YEAR, CustomDateTime, CURRENT_DATE);
    BaseDate = DATEADD(YEAR, ElapsedYears, CustomDateTime);
    
    -- 调整年数:如果加年数后的日期晚于当前日期,说明还没到周年
    IF (BaseDate > CURRENT_DATE) THEN
    BEGIN
        ElapsedYears = ElapsedYears - 1;
        BaseDate = DATEADD(YEAR, ElapsedYears, CustomDateTime);
    END;
    -- 特殊处理:闰年2月29日到平年2月28日,视为满一年
    ELSE IF (EXTRACT(MONTH FROM CustomDateTime) = 2 AND EXTRACT(DAY FROM CustomDateTime) = 29) THEN
    BEGIN
        IF (EXTRACT(MONTH FROM CURRENT_DATE) = 2 AND EXTRACT(DAY FROM CURRENT_DATE) = 28) THEN
        BEGIN
            ElapsedMonths = 0;
            ElapsedDays = 0;
            SUSPEND;
        END;
    END;

    -- 计算月数
    ElapsedMonths = DATEDIFF(MONTH, BaseDate, CURRENT_DATE);
    TempDate = DATEADD(MONTH, ElapsedMonths, BaseDate);
    
    -- 调整月数:如果加月数后的日期晚于当前日期,说明还没到整月
    IF (TempDate > CURRENT_DATE) THEN
    BEGIN
        ElapsedMonths = ElapsedMonths - 1;
        TempDate = DATEADD(MONTH, ElapsedMonths, BaseDate);
    END;

    -- 计算剩余天数
    ElapsedDays = DATEDIFF(DAY, TempDate, CURRENT_DATE);

    SUSPEND;
END;

关键逻辑说明

  • 闰年2月29日处理:当指定日期是2月29日,当前日期是平年的2月28日时,直接视为满一年,月数和天数都为0
  • 年数调整:通过DATEADD(YEAR, ElapsedYears, CustomDateTime)得到理论周年日期,如果这个日期晚于当前日期,说明还没到周年,年数减1
  • 月数调整:和年数调整逻辑一致,避免出现“30天的月份到28天月份”导致的月数计算错误

测试几个边界案例

  • 测试1:EXECUTE PROCEDURE CALCULATE_ELAPSED_TIME('2020-02-29')(当前日期为2021-02-28)→ 返回1年0月0日
  • 测试2:EXECUTE PROCEDURE CALCULATE_ELAPSED_TIME('2020-03-01')(当前日期为2023-02-28)→ 返回2年11月28日
  • 测试3:EXECUTE PROCEDURE CALCULATE_ELAPSED_TIME('2022-05-15')(当前日期为2024-06-20)→ 返回2年1月5日
  • 测试4:EXECUTE PROCEDURE CALCULATE_ELAPSED_TIME('2024-01-01')(当前日期为2023-12-31)→ 返回0年0月0日

内容的提问来源于stack exchange,提问作者Steve

火山引擎 最新活动