负数与 MONTH 日期部分一起使用时的 DATEADD 问题

DATEADD issue when negative number is used with MONTH datepart

来自 DATEADD 文档:

If datepart is month and the date month has more days than the return month and the date day does not exist in the return month, the last day of the return month is returned. For example, September has 30 days; therefore, the two following statements return 2006-09-30 00:00:00.000:

SELECT DATEADD(month, 1, '2006-08-30');

SELECT DATEADD(month, 1, '2006-08-31');

SQL Server知道2016-03的最后一天是312016-04的最后一天是30:

SELECT DAY(EOMONTH('2016-03-01')) -- 31
SELECT DAY(EOMONTH('2016-04-01')) -- 30

那么为什么会这样:

SELECT DATEADD(MONTH, -1, '2016-04-30')

returns 2016-03-30 00:00:00.000 而不是 2016-03-31 00:00:00.000?

此外,如果我有以下情况:

SELECT  DATEADD(MONTH, -1, '2016-03-31')

它正确 returns 2016-02-29 00:00:00.000.

它也不适用于从 30 -> 31 的正数,例如:

SELECT DATEADD(MONTH, 1, '2016-09-30')

产量 2016-10-30 00:00:00.000 而不是 2016-10-31 00:00:00.000

大概这是因为你不能在有 30 天的一个月中有第 31 天,它会溢出日期字段,但是你可以在有 31 天的一个月中有一个月的第 30 天。

dateadd 函数的简单工作流程

即。 对于 Day - 这是添加日(下一个有效日) 对于一周 - 它是添加周(接下来的 7 天) 对于月份 - 添加月份(仅限下个月,如果日期无效则向前填写) 对于年份 - 添加年份

SELECT DATEADD(MONTH, -1, '2016-03-31')</p> <p>输出: 2016-02-29 00:00:00.000

在你的例子中:

2016-03-31 -- 2016-02-31(减去 1 个月)但这不是有效日期,因此函数尝试向后填充(负数)\向前填充日期(正数)。 所以下一个有效的向后日期是 2016-02-29。

总结: 函数用于向前填充和向后填充。

正如其他评论所说,如果上个月存在该月的第几天,DATEADD 将使用它,而不是假设您想要 "the last day of the month"。

如果您确实想要 "Last day of the month",您将不得不增加一些逻辑,例如:

DECLARE @date DATETIME = '30 April 2016'
SELECT CASE WHEN DATEDIFF(MONTH, @date, DATEADD(DAY, 1, @date)) = 1 THEN     DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, @date), 0))
        ELSE DATEADD(MONTH, -1, @date)
   END

这将假设如果它是该月的最后一天,您需要上个月的最后一天,而不是明确的 28 日、29 日或 30 日。