Проблема с DATEADD при использовании отрицательного числа с параметром MONTH datepart

Из документации DATEADD:

Если datepart имеет значение month, а в месяце даты больше дней, чем в месяце возврата, и день даты не существует в месяце возврата, возвращается последний день месяца возврата. Например, в сентябре 30 дней; поэтому два следующих оператора возвращают 2006-09-30 00: 00: 00.000:

ВЫБЕРИТЕ ДОБАВЛЕНИЕ ДАТЫ (месяц, 1, '2006-08-30');

ВЫБЕРИТЕ ДОБАВЛЕНИЕ ДАТЫ (месяц, 1, '2006-08-31');

SQL Server знает, что последний день 2016-03 31, а последний день 2016-04 30:

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

Тогда почему следующее:

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

возвращает 2016-03-30 00:00:00.000 вместо 2016-03-31 00:00:00.000?

Также, если у меня есть следующее:

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

он правильно возвращает 2016-02-29 00:00:00.000.


person gotqn    schedule 14.04.2016    source источник
comment
Ответ есть в самом вашем вопросе :)   -  person Abdul Rasheed    schedule 14.04.2016
comment
... и день даты не существует в месяце возврата ... - поскольку 30-й день действительно существует в марте, он правильно возвращает 30-е марта   -  person pastacool    schedule 14.04.2016
comment
если вам недостаточно функции EOMONTH, и вы хотите 'придерживаться какого-то подхода типа DATEADD, вы можете использовать что-то вроде SELECT DATEADD(MM, DATEDIFF(MM, -1, GETDATE()), 0) - 1 LastDayOfMonth   -  person pastacool    schedule 14.04.2016
comment
Если вам всегда нужен последний день месяца, всегда используйте 31-е число a месяца в качестве отправной точки. Это так просто. (Или вместо этого вычислите 1-й день следующего месяца и используйте <, а не <=)   -  person Damien_The_Unbeliever    schedule 14.04.2016


Ответы (3)


простой рабочий процесс для функции dateadd

т.е. для дня - это добавленный день (следующий действительный день) для недели - это добавленная неделя (следующие 7 дней) для месяца - это добавленный месяц (только следующий месяц и заполнение вперед, если дата недействительна) для года - это добавленный год

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

output: 2016-02-29 00:00:00.000

в вашем примере:

2016-03-31 - 2016-02-31 (вычтите 1 месяц), но это недействительная дата, поэтому функция пытается получить обратное заполнение (отрицательное значение) \ заполнить дату вперед (положительное значение). поэтому следующая допустимая обратная дата - 29 февраля 2016 года.

заключение: используется функция заполнения вперед и заполнения назад.

person Sameer Morani    schedule 14.04.2016

Как сказано в других комментариях, если день месяца существует в предыдущем месяце, DATEADD будет использовать его, а не предполагать, что вы хотите «последний день месяца».

Если вам действительно нужен «Последний день месяца», вам придется добавить немного больше логики, например:

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-е.

person George Dando    schedule 14.04.2016

Он также не работает с положительными числами от 30 до 31, например:

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

Дает 2016-10-30 00:00:00.000 вместо 2016-10-31 00:00:00.000

Предположительно это связано с тем, что у вас не может быть 31-го дня в месяце с 30 днями, это приведет к переполнению поля даты, однако у вас может быть 30-е число в месяце с 31 днем.

person Simon    schedule 14.04.2016
comment
Это работает, вы ищете дату через месяц от даты начала, поэтому месяц с 2016-09-30 - 2016-10-30. DATEADD не предназначен для получения значений на конец месяца. Так получилось, что отсчет месяца отныне для дат 20xx-08-30 и 20xx-08-31 совпадает с 20xx-09-30. - person Dariusz Bielak; 14.04.2016