2013-03-01 2 views
1

Я разрабатываю систему календаря, в которой создаются события. Мне нужно иметь возможность «перематывать» любое событие (которое происходит в один день) в заданный пользователем месяц/год.Как найти будущую дату на основе соответствующего рабочего дня?

Например, 4 марта 2013 года - понедельник. Я должен уметь определять, по данному месяцу/году, какова будет соответствующая дата - на основе буднего дня и его позиции в течение месяца. Таким образом, в этом примере соответствующая дата апреля составит 1 апреля, т.е. понедельник.

Другой пример: 13 марта 2013 является среда, поэтому соответствующая дата в мае будет 8 мая

Если бы не тот факт, что поставки пользователь месяц/год является переменной, то это не будет такая сложная задача; но так как это ...

+0

как вы хотите иметь дело с этим делом: суббота, неделя 5 месяца. Не все месяцы имеют субботу на неделе 5, так что вы хотите использовать субботу недели 4? Я просто замечаю, что вы не идете по неделям, а через какое-то время. так что по-прежнему тот же принцип, не все месяцы имеют 5-ю субботу, так что в таком случае вы хотели бы использовать дату 4-го? – SwissCoder

+0

Нет, дни просто упадут. – user2122092

ответ

1

Если вы имели Dates таблицу, содержащую пять столбцов, FullDate, Month, Day, Year и DayOfWeek и заселена с датами в будущем вы могли бы легко сделать следующее.

Предполагая @m и @y являются задаваемый пользователем месяц/год катиться вперед, и @d является дата события:

DECLARE @weekNumInMonth int = 
(
    SELECT COUNT(1) 
    FROM Dates 
    WHERE Year = datepart(year @d) 
    AND Month = datepart(month, @d) 
    AND DayOfWeek = datepart(weekday, @d) 
    AND Day <= datepart(day, @d) 
) 

SELECT MAX(FullDate) 
FROM 
(
    SELECT TOP @weekNumInMonth 
    FROM Dates 
    WHERE Year = @y 
    AND Month = @m 
    AND DayOfWeek = datepart(weekday, @d) 
) x 

Без таблицы дат, вы просто должны сделать некоторые математические:

DECLARE @DOW int = datepart(weekday, @d) 
DECLARE @firstDayInMonth date = dateadd(day, 1-datepart(day, @d), @d) 
DECLARE @firstDayInMonthDOW int = datepart(weekday, @firstDayInMonth) 
DECLARE @firstSameDayInMonth date = 
    dateadd(day, (7-(@[email protected]))%7, @firstDayInMonth) 
DECLARE @weekInMonth int = datediff(week, @firstSameDayInMonth, @d) 

DECLARE @corr date = datefromparts(@y, @m, 1) 
DECLARE @corrDOW int = datepart(weekday, @corr) 
DECLARE @corrFirstSameDay date = dateadd(day, (7-(@[email protected]))%7, @corr) 

SELECT dateadd(week, @weekInMonth, @corrFirstSameDay) 

SQL Fiddle example

Это немного некрасиво, но то, что он делает:

  1. Получить первый день месяца, с тем же день недели, как @d в @firstSameDayInMonth.
  2. Укажите, какая неделя # @d находится в пределах соответствующего месяца, как целое число 0 @weekInMonth. Это число недель между @firstSameDayInMonth и @d.
  3. Получить первый день месяца @m, год @y с таким же днём по неделям как @d в @corrFirstSameDay.
  4. Добавить 0 основываясь на количестве недель @weekInMonth на @corrFirstSameDay, чтобы получить результат.

Вы можете сделать это как однострочный? Конечно, просто замените переменные. Имейте в виду, хотя, это некрасиво, и там действительно ничего можно извлечь из него, за исключением отсутствия читаемости ИМХО:

SELECT dateadd(week, datediff(week, dateadd(day, (7-(datepart(weekday, dateadd(day, 
    1-datepart(day, @d), @d))-datepart(weekday, @d)))%7, dateadd(day, 
    1-datepart(day, @d), @d)), @d), dateadd(day, (7-(datepart(weekday, 
    datefromparts(@y, @m, 1))-datepart(weekday, @d)))%7, datefromparts(@y, @m, 1))) 
+0

спасибо. Это было очень полезно! Просто примечание, ваш последний «уродливый» оператор выбора дает разные результаты из первого примера. Кроме того, datefromparts только на 2012 год.Еще раз спасибо! – user2122092

+0

@ user2122092 Возможно, это была плохая копия/вставка. И да, datefromparts только для 2012 года, но это делает его более читаемым, если вы можете и избегаете преобразования строк. При необходимости замените. –