Получить даты из номера недели в T-SQL

В Microsoft SQL Server у меня есть номер недели

(from DATEPART(wk, datecol)) 

но то, что я хотел бы сделать, это превратить это обратно в промежуток времени для этой недели.

Например,

SELECT DATEPART(wk, GETDATE())

дает 10. Я хотел бы вывести 3/1/2009 и 3/7/2009 из этого числа.

Это возможно?

21 ответ

Решение

Ответ Quassnoi работает, но отчасти оставляет вас на крючке для очистки дат, если они являются датами в середине дня (его начало недели оставляет вас на один день раньше, чем вам нужно, если вы используете время в середине дня - вы можете проверить с помощью GETDATE()).

Я использовал что-то подобное в прошлом:

SELECT 
   CONVERT(varchar(50), (DATEADD(dd, @@DATEFIRST - DATEPART(dw, DATECOL), DATECOL)), 101),
   CONVERT(varchar(50), (DATEADD(dd, @@DATEFIRST - DATEPART(dw, DATECOL) - 6, DATECOL)), 101)

Дополнительным преимуществом этого является то, что с помощью @@DATEFIRST вы можете обрабатывать нестандартные дни начала недели (по умолчанию воскресенье, но с помощью SET @@DATEFIRST вы можете изменить это).

Кажется сумасшедшим, что простое манипулирование датами в SQL Server должно быть таким загадочным, но вы идете...

Вы можете установить для @WeekNum и @YearNum все, что вам нужно - в этом примере они получены из переменной @datecol, для которой в качестве иллюстрации задано GETDATE(). Если у вас есть эти значения, вы можете рассчитать диапазон дат на неделю, используя следующие:

DECLARE @datecol datetime = GETDATE();
ОБЪЯВИТЬ @WeekNum INT, @YearNum char (4);

SELECT @WeekNum = DATEPART (WK, @datecol), @YearNum = CAST (DATEPART (YY, @datecol) AS CHAR (4));

- как только вы установили @WeekNum и @YearNum, следующий рассчитывает диапазон дат.
SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + @YearNum) + (@WeekNum-1), 6) AS StartOfWeek;
SELECT DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + @YearNum) + (@WeekNum-1), 5) AS EndOfWeek; 

Чтобы ответить на ваш вопрос:

--CHANGE A WEEK NUMBER BACK INTO A DATE FOR THE FIRST DATE OF THE WEEK
DECLARE @TaskWeek INT = 17
DECLARE @TaskYear INT = 2013

SELECT DATEADD(WEEK, @TaskWeek - 1,DATEADD(dd, 1 - DATEPART(dw, '1/1/' + CONVERT(VARCHAR(4),@TaskYear)), '1/1/' + CONVERT(VARCHAR(4),@TaskYear)))

Если ваша неделя начинается с понедельника (на SQL Server 2008)

select datecol,
  DATEPART(ISOWK, datecol) as week,
  ((DATEPART(dw, datecol)+5)%7)+1 as weekday,
  (DATEADD(dd, -((DATEPART(dw, datecol)+5)%7), datecol)) as Monday,
  (DATEADD(dd, -((DATEPART(dw, datecol)+5)%7)+6, datecol)) as Sunday

Как насчет функции, которая переходит на неделю до номера этой недели, а затем переходит на следующие несколько дней, пока не изменится номер недели (не более 7 шагов), возвращая новую дату?

CREATE FUNCTION dbo.fnGetDateFromWeekNo
(@weekNo int , @yearNo  int)
RETURNS smalldatetime
AS
BEGIN 

DECLARE @tmpDate smalldatetime


set @tmpdate= cast(cast (@yearNo as varchar) + '-01-01' as smalldatetime)
-- jump forward x-1 weeks to save counting through the whole year 
set @tmpdate=dateadd(wk,@weekno-1,@tmpdate)

-- make sure weekno is not out of range
if @WeekNo <= datepart(wk,cast(cast (@yearNo as varchar) + '-12-31' as smalldatetime))
BEGIN
    WHILE (datepart(wk,@tmpdate)<@WeekNo)
    BEGIN
        set @tmpdate=dateadd(dd,1,@tmpdate)
    END
END
ELSE
BEGIN
    -- invalid weeknumber given
    set @tmpdate=null
END


RETURN @tmpDate

END
SELECT DATECOL - DATEPART(weekday, DATECOL), DATECOL - DATEPART(weekday, DATECOL) + 7

Другой способ сделать это:

declare @week_number int;
declare @start_weekday int = 0 -- Monday
declare @end_weekday int = 6 -- next Sunday

select @week_number = datediff(week, 0, getdate())

select 
    dateadd(week, @week_number, @start_weekday) as WEEK_FIRST_DAY, 
    dateadd(week, @week_number, @end_weekday) as WEEK_LAST_DAY

Пояснение:

  • @week_number вычисляется на основе начальной календарной даты "1900-01-01". Замените getdate() любой датой.
  • @start_weekday - 0, если понедельник. Если воскресенье, объявите его как -1
  • @end_weekday - 6, если в следующее воскресенье. Если суббота, то объявите это как 5
  • затем dateadd функция, добавит заданное количество недель и заданное количество дней к начальной календарной дате "1900-01-01".

Я взял решение elindeblom и изменил его - использование строк (даже при приведении к датам) заставляет меня нервничать по поводу различных форматов дат, используемых во всем мире. Это позволяет избежать этой проблемы.

Хотя время не запрашивалось, я также включил время, поэтому неделя заканчивается за 1 секунду до полуночи:

    DECLARE @WeekNum INT = 12,
        @YearNum INT = 2014 ;

    SELECT  DATEADD(wk,
                    DATEDIFF(wk, 6,
                             CAST(RTRIM(@YearNum * 10000 + 1 * 100 + 1) AS DATETIME))
                    + ( @WeekNum - 1 ), 6) AS [start_of_week],
            DATEADD(second, -1,
                    DATEADD(day,
                            DATEDIFF(day, 0,
                                     DATEADD(wk,
                                             DATEDIFF(wk, 5,
                                                      CAST(RTRIM(@YearNum * 10000
                                                                 + 1 * 100 + 1) AS DATETIME))
                                             + ( @WeekNum + -1 ), 5)) + 1, 0)) AS [end_of_week] ;

Да, я знаю, что все еще кастую, но из числа. Мне это кажется безопаснее.

Это приводит к:

    start_of_week           end_of_week
    ----------------------- -----------------------
    2014-03-16 00:00:00.000 2014-03-22 23:59:59.000

Дайте это @Year и @Week, верните первое свидание на этой неделе.

Declare @Year   int
,@Week int
,@YearText varchar(4)

set @Year = 2009
set @Week = 10

set @YearText = @Year

print dateadd(day
             ,1 - datepart(dw, @YearText + '-01-01')
                + (@Week-1) * 7
             ,@YearText + '-01-01')
dateadd(
  dd, 
  datepart(wk, @Date)*7, 
  convert(smalldatetime, convert(char,year(max(@Date)))+convert(char, '-01-01'))
)-1 

Объявить @IntWeek как varchar (20) SET @IntWeek = '201820'

SELECT DATEADD (wk, DATEDIFF (wk, @@ DATEFIRST, LEFT (@ IntWeek, 4) + '- 01-01') + (приведение (RIGHT(@IntWeek,2) как int)-1), @@DATEFIRST) AS StartOfWeek

Это должно работать независимо от @@DATEFIRST

ALTER FUNCTION dbo.DEV_VW_WeekSerial
    (@YearNum int,
    @WeekNum int,
    @DayNum int)
    RETURNS Date AS

    BEGIN

        DECLARE @FirstDayYear As Date;

        SET @FirstDayYear='01/01/' + CAST(@YearNum As varchar)

        RETURN dateadd(d,(@DayNum-datepart(weekday,@FirstDayYear)),dateadd(week, @WeekNum-1,@FirstDayYear))

    END

Здесь вам просто нужно пройти год и номер недели.

DECLARE @Year VARCHAR(4)

SET @Year= '2012'

DECLARE @FirstDate DATETIME

SET @FirstDate = (SELECT DATEADD(dd,1,(SELECT DATEADD(wk,DATEPART(wk,GETDATE())-1,Convert(DAteTime,'01-01-' + @Year))))
                 )
DECLARE @LastDate DATETIME

SET @LastDate =(SELECT DATEADD(dd,4,@FirstDate))

SELECT @FirstDate
       ,@LastDate

Ответ большинства голосов работает отлично, за исключением 1-й недели и последней недели года. Если значение datecol равно "2009-01-01", результатом будет 01/03/2009 и 28.12.2008.

Мое решение:

DECLARE @Date date = '2009-03-01', @WeekNum int, @StartDate date;
SELECT @WeekNum = DATEPART(WEEK, @Date);
SELECT @StartDate = DATEADD(DAY, -(DATEPART(WEEKDAY, DATEADD(YEAR, DATEDIFF(YEAR, 0, @Date), 0)) + 6), DATEADD(YEAR, DATEDIFF(YEAR, 0, @Date), 0));
SELECT CONVERT(nvarchar, CASE WHEN @WeekNum = 1 THEN CAST(DATEADD(YEAR, DATEDIFF(YEAR, 0, @Date), 0) AS date) ELSE DATEADD(DAY, 7 * @WeekNum, @StartDate) END, 101) AS StartOfWeek
      ,CONVERT(nvarchar, CASE WHEN @WeekNum = DATEPART(WEEK, DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, @Date) + 1, 0))) THEN DATEADD(DAY, -1, DATEADD(YEAR, DATEDIFF(YEAR, 0, @Date) + 1, 0)) ELSE DATEADD(DAY, 7 * @WeekNum + 6, @StartDate) END, 101) AS EndOfWeek;

Это будет отображать 01/01/2009 и 01/03/2009 для 1-й недели, и отображать 03/01/2009 и 03/07/2009 для 10-й недели.

Я думаю, что это именно то, что вы хотите именно. Вы можете заменить переменные на их выражения по своему желанию.

Я просто включил SELECT с оператором CASE (для моей ситуации понедельник отметил первый день недели и не хотел иметь дело с командой SET DATEFIRST:

CASE DATEPART(dw,<YourDateTimeField>)
   WHEN 1 THEN CONVERT(char(10), DATEADD(DD, -6, <YourDateTimeField>),126) +  ' to ' + CONVERT(char(10), <YourDateTimeField>,126)
   WHEN 2 THEN CONVERT(char(10), <YourDateTimeField>,126) +  ' to ' + CONVERT(char(10), DATEADD(DD, 6, <YourDateTimeField>),126)
   WHEN 3 THEN CONVERT(char(10), DATEADD(DD, -1, <YourDateTimeField>),126) +  ' to ' + CONVERT(char(10), DATEADD(DD, 5, <YourDateTimeField>),126)
   WHEN 4 THEN CONVERT(char(10), DATEADD(DD, -2, <YourDateTimeField>),126) +  ' to ' + CONVERT(char(10), DATEADD(DD, 4, <YourDateTimeField>),126)
   WHEN 5 THEN CONVERT(char(10), DATEADD(DD, -3, <YourDateTimeField>),126) +  ' to ' + CONVERT(char(10), DATEADD(DD, 3, <YourDateTimeField>),126)
   WHEN 6 THEN CONVERT(char(10), DATEADD(DD, -4, <YourDateTimeField>),126) +  ' to ' + CONVERT(char(10), DATEADD(DD, 2, <YourDateTimeField>),126)
   WHEN 7 THEN CONVERT(char(10), DATEADD(DD, -5, <YourDateTimeField>),126) +  ' to ' + CONVERT(char(10), DATEADD(DD, 1, <YourDateTimeField>),126)
   ELSE 'UNK'
END AS Week_Range

Решение с:

  • Поддержка 1-й и последней недели года
  • Поддержка понедельника или воскресенья как 1-го дня недели.
  • Никаких манипуляций со строками
      SET DATEFIRST 1; -- Optionnal : if monday is your 1st day of the week

DECLARE @year INT = 2022;
DECLARE @week INT = 1;

SELECT [WeekStart] = CASE @week WHEN 1 THEN DATEADD(YEAR, @year - 1900, 0) ELSE DATEADD(DAY, 1 - DATEPART(WEEKDAY, DATEADD(WEEK, @week, DATEADD(YEAR, @year - 1900, 0))), DATEADD(WEEK, @week - 1, DATEADD(YEAR, @year - 1900, 0))) END
SELECT [WeekEnd] = CASE @week WHEN 53 THEN DATEADD(DAY, -1, DATEADD(YEAR, @year + 1 - 1900, 0)) ELSE DATEADD(DAY, -DATEPART(WEEKDAY, DATEADD(WEEK, @week, DATEADD(YEAR, @year - 1900, 0))), DATEADD(WEEK, @week, DATEADD(YEAR, @year - 1900, 0))) END

например, с 2022 годом, неделя 1:

Протестировано с датами с 2023 по 2035 год .

DECLARE @dayval int,
 @monthval int,
 @yearval int

SET @dayval = 1
SET @monthval = 1
SET @yearval = 2011


DECLARE @dtDateSerial datetime

        SET @dtDateSerial = DATEADD(day, @dayval-1,
                                DATEADD(month, @monthval-1,
                                    DATEADD(year, @yearval-1900, 0)
                                )
                            )

DECLARE @weekno int
SET @weekno = 53


DECLARE @weekstart datetime
SET @weekstart = dateadd(day, 7 * (@weekno -1) - datepart (dw, @dtDateSerial), @dtDateSerial)

DECLARE @weekend datetime
SET @weekend = dateadd(day, 6, @weekstart)

SELECT @weekstart, @weekend

Это работает для меня:

select 
    convert(varchar(50), dateadd(dd, - datepart(dw, DATECOL) + 1, DATECOL), 101),
    convert(varchar(50), dateadd(dd, - datepart(dw, DATECOL) + 7, DATECOL), 101)

Я не потратил время, чтобы проверить каждый ответ здесь, но ничто не кажется таким простым и эффективным, как это:

DECLARE @WeekNum int
DECLARE @YearNum char(4)

SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + @YearNum) + (@WeekNum-1), 6) AS StartOfWeek

SELECT DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + @YearNum) + (@WeekNum-1), 5) AS EndOfWeek

Ответ:

select  DateAdd(day,-DATEPart(DW,<Date>), <Date>) [FirstDayOfWeek] ,DateAdd(day,-DATEPart(DW,<Date>)+6, <Date>) [LastDayOfWeek]
FROM <TABLE>
SELECT DATEADD(week, @weekNumber - 1, DATEADD(DAY, @@datefirst - DATEPART(weekday, CAST(YEAR(GETDATE()) AS VARCHAR) + '-01-01') - 6, CAST(YEAR(GETDATE()) AS VARCHAR) + '-01-01'))
Другие вопросы по тегам