Obter datas a partir de um número de semana em T-SQL
-
03-07-2019 - |
Pergunta
Em Microsoft SQL Server, eu tenho um número da semana
(from DATEPART(wk, datecol))
mas o que eu gostaria de fazer é transformar isso de volta para o intervalo data para essa semana.
Por exemplo,
SELECT DATEPART(wk, GETDATE())
produz 10. Gostaria de obter 3/1/2009 e 3/7/2009 a partir deste número.
Isso é possível?
Solução
obras resposta do Quassnoi, mas tipo de deixa-lo no gancho para limpar as datas se eles são datas no meio do dia (o início da semana deixa você um dia mais cedo do que você precisa para estar se você usar um tempo no meio do dia -. você pode testar usando GETDATE ())
Eu usei algo parecido com isso no passado:
SELECT
CONVERT(varchar(50), (DATEADD(dd, @@DATEFIRST - DATEPART(dw, DATECOL), DATECOL)), 101),
CONVERT(varchar(50), (DATEADD(dd, @@DATEFIRST - DATEPART(dw, DATECOL) - 6, DATECOL)), 101)
Um benefício colateral disso é que, ao usar @@ DATEFIRST você pode manipular semana começando dias fora do padrão (o padrão é domingo, mas com SET @@ DATEFIRST você pode mudar isso).
Parece loucura que a manipulação data simples em SQL Server tem de ser este arcano, mas lá vai ...
Outras dicas
Você pode definir @WeekNum e @YearNum para o que quiser - neste exemplo, eles são derivados da variável @datecol, que está previsto para GETDATE () para fins de ilustração. Depois de ter os valores- você pode calcular o intervalo de datas para uma semana usando o seguinte:
DECLARE @datecol datetime = GETDATE(); DECLARE @WeekNum INT , @YearNum char(4); SELECT @WeekNum = DATEPART(WK, @datecol) , @YearNum = CAST(DATEPART(YY, @datecol) AS CHAR(4)); -- once you have the @WeekNum and @YearNum set, the following calculates the date range. 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;
Para responder à sua pergunta:
--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)))
Se o seu semana começa a partir de segunda-feira (no 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
SELECT DATECOL - DATEPART(weekday, DATECOL), DATECOL - DATEPART(weekday, DATECOL) + 7
Como sobre uma função que salta para a semana antes que o número semana e, em seguida, percorre os próximos dias até que as alterações número semana (máximo 7 etapas), retornando a nova data?
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
dateadd(
dd,
datepart(wk, @Date)*7,
convert(smalldatetime, convert(char,year(max(@Date)))+convert(char, '-01-01'))
)-1
Aqui você só tem que passar ano e número da semana.
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
Isso deve funcionar independentemente de @@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
Eu tomei a solução da elindeblom e modificado - a utilização de cordas (mesmo elenco para datas) me deixa nervoso para os diferentes formatos de datas utilizadas em todo o mundo. Isso evita esse problema.
Embora não seja solicitado, eu também incluiu tempo para os fins de semana 1 segundo antes da meia-noite:
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] ;
Sim, eu sei que ainda estou lançando mas a partir de um número. "Sente" mais seguro para mim.
Isso resulta em:
start_of_week end_of_week
----------------------- -----------------------
2014-03-16 00:00:00.000 2014-03-22 23:59:59.000
Dê-lhe @Year e @Week, retornar primeiro encontro dessa semana.
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')
Eu só incorporou o SELECT com uma instrução CASE (Para minha situação segunda-feira marcou o primeiro dia da semana, e não queria lidar com o comando 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
Os votos resposta mais funciona bem, exceto o 1ª semana e na semana passada do ano. Quando datecol valor é '2009-01-01' , o resultado será 01/03/2009 e 12/28 / 2008 .
A minha solução:
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;
Isto irá exibir 01/01/2009 e 01/03/2009 para a 1ª semana, e exibição 03/01/2009 e 03/07/2009 para a 10ª semana.
Eu acho que isso seria o que você quer exatamente. Você pode substituir as variáveis ??com suas expressões como desejar.
declare @IntWeek as varchar(20)
SET @IntWeek = '201820'
SELECT
DATEADD(wk, DATEDIFF(wk, @@DATEFIRST, LEFT(@IntWeek,4) + '-01-01') +
(cast(RIGHT(@IntWeek, 2) as int) -1), @@DATEFIRST) AS StartOfWeek
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'))
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
Resposta:
select DateAdd(day,-DATEPart(DW,<Date>), <Date>) [FirstDayOfWeek] ,DateAdd(day,-DATEPart(DW,<Date>)+6, <Date>) [LastDayOfWeek]
FROM <TABLE>
Isso funciona para mim:
select
convert(varchar(50), dateadd(dd, - datepart(dw, DATECOL) + 1, DATECOL), 101),
convert(varchar(50), dateadd(dd, - datepart(dw, DATECOL) + 7, DATECOL), 101)
Eu não tomar o tempo para testar cada resposta aqui, mas nada parece tão simples e tão eficiente como este:
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