На основе идеи, опубликованной в этом ответе here Я создал следующую схему и запрос для расчета правильной даты.
Запрос будет преобразован в функцию со встроенной таблицей, которая будет принимать параметры @Id
и @RelativeToDate
.
DayOfWeekNumber
будет хранить значения от 1 до 7, где 1 = понедельник и 7 = воскресенье, функция должна рассчитывать правильный результат независимо от @@DATEFIRST
.
В настоящее время ограничений в таблице нет, но на самом деле должно быть необходимо обеспечить наличие только достоверных записей в таблице.
DECLARE @RelativeDate TABLE
(
Id INT IDENTITY(1,1)
, Name VARCHAR(100)
, DayOfWeekNumber TINYINT
, DayOfMonthNumber TINYINT
, LastDayOfMonth BIT
, DayOffset SMALLINT
, WeekOffset SMALLINT
, MonthOffset SMALLINT
, YearOffset SMALLINT
)
INSERT INTO @RelativeDate
(
Name
, DayOfWeekNumber
, DayOfMonthNumber
, LastDayOfMonth
, DayOffset
, WeekOffset
, MonthOffset
, YearOffset
)
VALUES
('1st of previous month', 0, 1, 'FALSE', 0, 0, -1, 0)
, ('Last of previous month', 0, 0, 'TRUE', 0, 0, -1, 0)
, ('1st of current month', 0, 1, 'FALSE', 0, 0, 0, 0)
, ('Last of current month', 0, 0, 'TRUE', 0, 0, 0, 0)
, ('7th of current month', 0, 7, 'FALSE', 0, 0, 0, 0)
, ('8th of current month', 0, 8, 'FALSE', 0, 0, 0, 0)
, ('14th of current month', 0, 14, 'FALSE', 0, 0, 0, 0)
, ('21th of current month', 0, 21, 'FALSE', 0, 0, 0, 0)
, ('22th of current month', 0, 22, 'FALSE', 0, 0, 0, 0)
, ('28th of current month', 0, 28, 'FALSE', 0, 0, 0, 0)
, ('29th of current month', 0, 29, 'FALSE', 0, 0, 0, 0)
, ('This Sunday', 7, 0, 'FALSE', 0, 0, 0, 0)
, ('Next Sunday', 7, 0, 'FALSE', 0, 1, 0, 0)
, ('Last Sunday', 7, 0, 'FALSE', 0, -1, 0, 0)
DECLARE
@Date DATE = GETDATE()
SELECT
*
, DATENAME(WEEKDAY, DateValue) AS WeekDayName
FROM
@RelativeDate
CROSS APPLY(VALUES(
DATEADD(DAY, DayOffset,
DATEADD(WEEK, WeekOffset,
DATEADD(MONTH, MonthOffset,
DATEADD(YEAR, YearOffset, @Date))))
)) AS OffsetDate(OffsetDate)
CROSS APPLY(VALUES(
CASE
WHEN LastDayOfMonth = 'TRUE'
THEN DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, OffsetDate) + 1, 0))
-- If the month doesn't have the day of the month number then return NULL
WHEN DayOfMonthNumber <> 0 AND DayOfMonthNumber > DATEPART(DAY, DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, OffsetDate) + 1, 0)))
THEN NULL
WHEN DayOfMonthNumber <> 0 AND DATEPART(DAY, OffsetDate) > DayOfMonthNumber
THEN DATEADD(DAY, DayOfMonthNumber - DATEPART(DAY, OffsetDate), OffsetDate)
WHEN DayOfMonthNumber <> 0 AND DATEPART(DAY, OffsetDate) < DayOfMonthNumber
THEN DATEADD(DAY, DayOfMonthNumber - DATEPART(DAY, OffsetDate), OffsetDate)
WHEN DayOfWeekNumber <> 0
THEN DATEADD(DAY, DayOfWeekNumber - (((DATEPART(WEEKDAY, OffsetDate) + @@DATEFIRST - 1 - 1) % 7) + 1), OffsetDate)
END
)) AS DateValue(DateValue)
Я не уверен, что понял здесь вопрос? Вы пытаетесь выяснить, как найти такие вещи, как последний день предыдущего месяца и что-то вроде этого? Вот ссылка на ряд общих подпрограмм datetime. http://www.sqlservercentral.com/blogs/lynnpettis/2009/03/25/some-common-date-routines/ –
См. обновленный вопрос. – mheptinstall
Один из способов представления относительных дат: http://programmers.stackexchange.com/a/78883/111526 – mheptinstall