PAYATHON 2026

在 SQL Server 2005 中计算最近的未来周期付款日期

支付阿杰

结论

首次付款日为 2009-06-01。先计算给定日期与首次付款日之间的天数,再按每 14 天一个付款周期取整。

如果 datetime 包含时间,还要比较候选付款时间和给定时间。候选时间已经过去时,应顺延 14 天。

推荐写法

以下写法适用于 SQL Server 2005,也能正确处理 datetime 中的时间部分:

DECLARE @firstPayment datetime;
DECLARE @fromDate     datetime;
DECLARE @candidate    datetime;

SET @firstPayment = '20090601';
SET @fromDate = '20090620';

IF @fromDate <= @firstPayment
BEGIN
    SET @candidate = @firstPayment;
END
ELSE
BEGIN
    SET @candidate = DATEADD(
        day,
        14 * (DATEDIFF(day, @firstPayment, @fromDate) / 14),
        @firstPayment
    );

    IF @candidate < @fromDate
        SET @candidate = DATEADD(day, 14, @candidate);
END;

SELECT @candidate AS NextPaymentDate;

当 @fromDate = '20090620' 时,结果是:

2009-06-29 00:00:00.000

当 @fromDate = '20090612' 时,结果是:

2009-06-15 00:00:00.000

如果给定日期恰好是付款日,例如 2009-06-15 00:00:00,则仍然返回 2009-06-15。这符合“当天或之后最近的付款日期”这一要求。

在查询中使用

给定日期保存在变量中时,可以通过 CROSS APPLY 计算候选日期,再将结果用于 WHERE 条件:

DECLARE @firstPayment datetime;
DECLARE @fromDate     datetime;

SET @firstPayment = '20090601';
SET @fromDate = '20090620';

SELECT t.*
FROM dbo.YourTable AS t
CROSS APPLY
(
    SELECT CASE
        WHEN @fromDate <= @firstPayment
            THEN @firstPayment
        ELSE DATEADD(
            day,
            14 * (DATEDIFF(day, @firstPayment, @fromDate) / 14),
            @firstPayment
        )
    END AS CandidateDate
) AS c
CROSS APPLY
(
    SELECT CASE
        WHEN c.CandidateDate < @fromDate
            THEN DATEADD(day, 14, c.CandidateDate)
        ELSE c.CandidateDate
    END AS NextPaymentDate
) AS p
WHERE t.someDate < p.NextPaymentDate;

具体计算分为四步:

  1. 用 DATEDIFF(day, ...) 计算首次付款日与给定日期之间的天数。
  2. 用整数除法 / 14 计算已经过去的完整付款周期数。
  3. 用 DATEADD 算出当前周期对应的付款日期。
  4. 如果这个付款时间早于给定时间,就再增加 14 天。

仅处理日期时的简化写法

如果输入不包含时间部分,并且不会早于首次付款日,可以直接写成:

DATEADD(
    day,
    14 * ((DATEDIFF(day, '20090601', @fromDate) + 13) / 14),
    '20090601'
)

例如:

DECLARE @fromDate datetime;
SET @fromDate = '20090620';

SELECT DATEADD(
    day,
    14 * ((DATEDIFF(day, '20090601', @fromDate) + 13) / 14),
    '20090601'
) AS NextPaymentDate;

其中,+ 13 用来实现向上取整:

周期数 = CEILING(相差天数 / 14)

SQL Server 2005 中,两个整数相除时执行整数除法。因此,对于正整数,可以用下面的公式模拟向上取整:

(相差天数 + 13) / 14

注意事项

  • 日期常量建议写成 '20090601' 这样的 YYYYMMDD 格式,避免受到 SQL Server 会话语言和 DATEFORMAT 设置的影响。
  • 简化公式只适用于不早于首次付款日的日期。如果输入可能早于 2009-06-01,应使用 CASE 直接返回首次付款日。
  • 如果输入包含时间,例如 2009-06-15 10:00:00,那么当天的付款时间 2009-06-15 00:00:00 已经过去,下一次付款应为 2009-06-29 00:00:00。此时应使用推荐写法,不能只按天数套用向上取整公式。
  • 如果业务要求返回“严格晚于给定日期”的付款时间,即输入恰好等于付款时刻时也要返回下一周期,应将相关比较由 < 改为 <=。

备注:内容仅供参考。