A semana começa no domingo ou na segunda-feira?

 O dia 7 de Janeiro de 2024 está na primeira ou na segunda semana do ano de 2024?
Se o final de semana compreende sábado e domingo, porque que no calendário vem “Domingo” como início de cada semana?

E o sql server, trata corretamente as semanas do ano?

1. Introdução

1.1. A dúvida

Geralmente publico aqui no blog artigos que ocupam várias páginas; é que gosto de aprofundar no assunto de cada artigo. Certa época participei de fóruns de sql server, respondendo a dúvidas postadas pelos usuários dos fóruns (isto inclusive está descrito no artigo “Sete anos de participação em fóruns SQL Server do MSDN & TechNet”) e algumas dessas dúvidas geraram artigos aqui no blog.

Há pouco, em grupo de sql server no telegram foi postada a seguinte dúvida:

Pela simplicidade da solução, não achei que seria algo para escrever artigo. Entretanto, como fiquei um bom tempo sem publicar artigos, percebi que talvez fosse o momento de iniciar uma série de artigos curtos – shorts, tão em moda atualmente para vídeos publicados em redes sociais.

Com relação ao assunto deste artigo – a partir de semana/ano obter qual o período do ano referente à semana – de imediato vieram duas dúvidas sobre o que seria “semana”:

    • conjuntos de 7 dias consecutivos, a partir do início do ano; ou
    • semana da folhinha (calendário)?

E, no caso de semana da folhinha,

    • a semana inicia no domingo ou na segunda-feira?

Perguntei ao autor da mensagem mas, como ele não respondeu até o momento em que iniciei a escrever este artigo, resolvi atender ao caso “semana da folhinha”.

1.2. O que define a primeira semana do ano?

Encontrar as regras para saber qual é a primeira semana do ano é a mesma coisa que colocar a mão no vespeiro. É tudo tão confuso, inclusive com regras mutuamente exclusivas! Então, para simplificar a primeira parte deste artigo, é considerado que:

    • o dia 1 de Janeiro está na primeira semana do ano;
    • se o seu país considera que a semana começa na segunda-feira, então o primeiro domingo do ano está na primeira semana do ano;
    • mas se o seu país considera que a semana começa no domingo, então o primeiro sábado do ano está na primeira semana do ano.

Achou estranho? Espere a segunda parte deste artigo e que trata de ISO 8601, quando podem ocorrer fatos estranhos como, por exemplo, “a primeira semana de um ano ser a segunda pois o que seria a primeira semana é a última semana do ano anterior”.

Plágio. Antes de prosseguir, uma observação. Desenvolver códigos T-SQL exige tempo e dedicação para estudos e testes, afora conhecimento técnico. Sempre que utilizo código T-SQL de outra pessoa como base eu informo o nome do autor e/ou o endereço web da página onde foi publicado, de modo a respeitar o esforço de programação de quem o desenvolveu. Do contrário tem-se o plágio, que além de ser uma violação dos direitos autorais é também uma demonstração de falta de ética profissional. Nesse ponto faço minhas as palavras de Edvaldo Castro e de Erickson Ricci presentes no artigo “PLÁGIO – Sério mesmo?”.

1.3. DATEPART

A função DATEPART tem como objetivo retornar informações diversas sobre alguma expressão de tipo de dados que armazenem data e/ou hora. Dentre as várias informações disponíveis utilizaremos duas delas: week, weekday. Mas antes de iniciar na explicação dessas informações, cabe citar o cuidado que se deve ter ao utilizar a função DATEPART, o que inclusive consta na documentação. É que o valor retornado para algumas das informações depende da configuração de LANGUAGE e/ou DATEFORMAT e, no caso das informações que coletaremos, é preciso ficar atento à configuração de DATEFIRST pois o resultado pode ser diferente para diferentes valores de @@datefirst.

1.3.1. Influência de DATEFIRST

A configuração de DATEFIRST por ser feita direta ou indiretamente (quando se altera o LANGUAGE). Para este artigo vamos considerar a alteração direta de DATEFIRST, para simplificar o desenvolvimento do texto.

Sobre a informação week, ela retorna a qual semana pertence a data informada. Por exemplo:

-- código #1.1
-- © José Diz / Porto SQL

-- retorna a semana, dentro do ano, da data informada
declare @DataProc date;
set @DataProc= convert (date, '29/2/2020', 103);

SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (week, @DataProc) as Semana;

29 de Fevereiro?  Pois é, escolhi data que só ocorre em ano bissexto. Ao rodar o código sql acima obtém-se o seguinte resultado:

Para confirmar, peguei o calendário de 2020, meses de janeiro e fevereiro, e contei nos dedos as semanas: o dia 29 de Fevereiro está na nona semana. Entretanto, cometi um erro na programação: esqueci de configurar DATEFIRST. Acrescentei então o teste com os valores de datefirst: semana iniciando no domingo e na segunda-feira:

-- código #1.2
-- © José Diz / Porto SQL

-- retorna a semana, dentro do ano, da data informada
declare @DataProc date;
set @DataProc= convert (date, '29/2/2020', 103);

-- semana inicia no domingo
set DATEFIRST 7;
SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (week, @DataProc) as Semana, 
       @@datefirst as [datefirst];

-- semana inicia na segunda-feira
set DATEFIRST 1;
SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (week, @DataProc) as Semana,
       @@datefirst as [datefirst];

que retornou o seguinte resultado:

Para confirmar, eis a folhinha do mês de fevereiro de 2020, com semana iniciando no domingo (Brasil) e na segunda-feira (Portugal):

Ufa! Para essa data não há diferença ao considerar a semana iniciando no domingo ou na segunda-feira.

O que acontece se escolher o dia seguinte: 1 de Março de 2020?

-- código #1.3
-- © José Diz / Porto SQL

-- retorna a semana, dentro do ano, da data informada
declare @DataProc date;
set @DataProc= convert (date, '1/3/2020', 103);

-- semana inicia no domingo
set DATEFIRST 7;
SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (week, @DataProc) as Semana,
       @@datefirst as [datefirst];

-- semana inicia na segunda-feira
set DATEFIRST 1;
SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (week, @DataProc) as Semana,
       @@datefirst as [datefirst];

Isto:

Ops! O dia 1º de Março de 2020 fica em semanas diferentes, dependendo se considerarmos a semana iniciando no domingo ou na segunda-feira. Bom, agora confirmamos que temos que sempre configurar datefirst para obter a informação week de forma correta.

E o que ocorre com a informação weekday quando se altera datefirst? Basta alterar o código #1.3, substituindo week por weekday, para obtermos a resposta:

-- código #1.4
-- © José Diz / Porto SQL

-- retorna a semana, dentro do ano, da data informada
declare @DataProc date;
set @DataProc= convert (date, '1/3/2020', 103);

-- semana inicia no domingo
set DATEFIRST 7;
SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (weekday, @DataProc) as [Dia da semana],
       @@datefirst as [datefirst];

-- semana inicia na segunda-feira
set DATEFIRST 1;
SELECT convert (char(10), @DataProc, 103) as [Data], 
       datepart (weekday, @DataProc) as [Dia da semana],
       @@datefirst as [datefirst];

e pronto. Temos o seguinte resultado:

Para cada valor de datefirst, o valor retornado por weekday foi diferente: quando datefirst é 7 (semana iniciando no domingo), o valor 1 em dia da semana indica que é domingo; quando  datefirst é 1 (semana iniciando na segunda-feira), o valor em dia da semana é 7 e também indica que é domingo. Ou seja, o valor retornado por weekday também varia de acordo com a configuração de datefirst.

2. Desenvolvimento do código sql

2.1 Passo a passo

Após a exemplificação das diferenças retornadas por DATEPART, para week e weekday, podemos iniciar o desenvolvimento das soluções do problema: a partir de semana/ano obter qual o período do ano referente à semana.

-- código #2.1
-- © José Diz / Porto SQL

declare @Semana tinyint, @Ano smallint, @InicioDS tinyint,
@DataInicial date, @DataFinal date;
set @Semana= 9;
set @Ano= 2020;
set @InicioDS= 7; -- dia de semana que inicia a semana

-- procura primeiro dia da semana/ano alvo
set datefirst @InicioDS; 
set @DataInicial= datefromparts (@Ano, 1, 1);
while datepart (week, @DataInicial) < @Semana
  set @DataInicial= dateadd (day, +1, @DataInicial);

-- dia final da semana/ano alvo
set @DataFinal= dateadd (day, +6, @DataInicial);

--
SELECT cast (@Semana as varchar(2)) + '/' + cast (@Ano as char(4)) as [Semana/Ano],
       @InicioDS as [Semana inicia em], 
       convert (char(10), @DataInicial, 103) as [Data inicial], 
       convert (char(10), @DataFinal, 103) as [Data final];

No código sql anterior, a forma de obter o primeiro dia da semana alvo é bem tosca: um laço somando um dia de cada vez e então verificando se iniciou a semana. Depois vamos procurar formas mais “inteligentes” de encontrar o primeiro dia da semana. Uma vez encontrado o primeiro dia da semana, basta somar 6 para termos o último dia da semana. Bem simples, não?

Observe que a definição de DATEFIRST ocorreu através de uma variável; isso é para tornar o código sql mais fácil de ser utilizado, ao testarmos diferentes valores de DATEFIRST e observando o resultado.

Ao rodarmos o código sql #2.1 para datefirst 7 (semana iniciando em domingo) obtemos o seguinte resultado:

e para datefirst 1 (semana iniciando na segunda-feira) temos o seguinte resultado:

Ou seja, o período da semana 9, ano de 2020 varia dependendo de qual dia da semana se considera o início da semana.

Primeira semana do ano. Ao testar o código sql #2.1 com diferentes valores (semana, ano e dia de semana inicial) percebi que o período retornado na primeira e última semana do ano nem sempre estava correto. Por exemplo, considerando-se a semana iniciando em domingo, a primeira semana do ano de 2020 possui somente o dia 1 mas o código sql #2.1 retorna que a semana é do dia 1 ao dia 7/1/2020; isto não está correto. Então, o código sql precisa de ajustes no cálculo de data final, estando incorreto somar 6 dias para todas as situações…

A solução está na análise de weekday do primeiro dia da semana, variável @DataInicial, e então verificar qual é o primeiro dia em que o weekday atinge o valor 7. Pronto, já temos o último dia da semana.

-- código #2.2
-- © José Diz / Porto SQL

declare @Semana tinyint, @Ano smallint, @InicioDS tinyint,
        @DataInicial date, @DataFinal date;
set @Semana= 1;
set @Ano= 2020;
set @InicioDS= 7 -- dia de semana que inicia a semana

-- procura primeiro dia da semana/ano alvo
set datefirst @InicioDS; 
set @DataInicial= datefromparts (@Ano, 1, 1);
while datepart (week, @DataInicial) < @Semana
  set @DataInicial= dateadd (day, +1, @DataInicial);

-- dia final da semana/ano alvo
set @DataFinal= @DataInicial;
while (datepart (weekday, @DataFinal) < 7)
  set @DataFinal= dateadd (day, +1, @DataFinal);

--
SELECT cast (@Semana as varchar(2)) + '/' + cast (@Ano as char(4)) as [Semana/Ano],
       @InicioDS as [Semana inicia em], 
       convert (char(10), @DataInicial, 103) as [Data inicial], 
       convert (char(10), @DataFinal, 103) as [Data final];

com o seguinte resultado:

Última semana do ano. O ano de 2020 teve 53 semanas. Como se comporta o código #2.2 se solicitarmos o período de datas que compõem a última semana do ano de 2020?

Ops! Retornou data do ano seguinte, 2021. Então, de volta à prancheta para corrigir também esse caso particular, que é simples de resolver:

-- código #2.3
-- © José Diz / Porto SQL

declare @Semana tinyint, @Ano smallint, @InicioDS tinyint,
@DataInicial date, @DataFinal date;
set @Semana= 53;
set @Ano= 2020;
set @InicioDS= 7 -- dia de semana que inicia a semana

-- procura primeiro dia da semana/ano alvo
set datefirst @InicioDS; 
set @DataInicial= datefromparts (@Ano, 1, 1);
while datepart (week, @DataInicial) < @Semana
  set @DataInicial= dateadd (day, +1, @DataInicial);

-- dia final da semana/ano alvo
set @DataFinal= @DataInicial;
while (datepart (weekday, @DataFinal) < 7)
  set @DataFinal= dateadd (day, +1, @DataFinal);
IF (year (@DataFinal) > @Ano)
  set @DataFinal= datefromparts (@Ano, 12, 31);

--
SELECT cast (@Semana as varchar(2)) + '/' + cast (@Ano as char(4)) as [Semana/Ano],
       @InicioDS as [Semana inicia em], 
       convert (char(10), @DataInicial, 103) as [Data inicial], 
       convert (char(10), @DataFinal, 103) as [Data final];

Agora o resultado está correto:

2.2. Versão final

Agora que temos um código sql funcionando, é o momento de finalizá-lo. Primeiro, acrescentar o teste de validade dos parâmetros, que são as variáveis @Semana, @Ano e @InicioDS. Vai que algum engraçadinho solicita, por exemplo, semana 100… Outra finalização é a otimização do código sql, procurando eliminar os laços (while) utilizados.

-- código #2.4
-- Autor: José Diz
-- Publicado em: Porto SQL - https://portosql.wordpress.com

declare @Semana tinyint, @Ano smallint, @InicioDS tinyint,
        @DataInicial date, @DataFinal date;

----- informe aqui os parâmetros de execução
set @Semana= 9;
set @Ano= 2020;
set @InicioDS= 7 -- dia de semana que inicia a semana

------ analisa os parâmetros de execução
-- datefirst
IF not @InicioDS between 1 and 7
  begin
  PRINT 'Valor de @InicioDS deve ser entre 1 e 7';
  return
  end;

-- ano
IF @Ano < 1583
  begin
  PRINT 'Valor de ano inválido';
  return
  end;

-- semana do ano
set datefirst @InicioDS; 
IF (@Semana < 1) or 
   (@Semana > datepart (week, datefromparts (@Ano, 12, 31)))
  begin
  PRINT 'Valor de semana inválido';
  return
  end;

----- definição das datas inicial e final da semana/ano
set datefirst @InicioDS;

-- procura último dia da primeira semana
set @DataFinal= datefromparts (@Ano, 1, 1);
set @DataFinal= dateadd (day, (7 - datepart (weekday, @DataFinal)), @DataFinal);

-- calcula último dia da semana alvo
set @DataFinal= dateadd (week, (@Semana -1), @DataFinal);

-- define data inicial, a partir da data final
set @DataInicial= dateadd (day, -6, @DataFinal);

-- ajusta datas, se virada de ano
IF (year (@DataFinal) > @Ano)
  set @DataFinal= datefromparts (@Ano, 12, 31)
else 
IF (year (@DataInicial) < @Ano)
  set @DataInicial= datefromparts (@Ano, 1, 1);

----- apresentação do resultado
SELECT cast (@Semana as varchar(2)) + '/' + cast (@Ano as char(4)) as [Semana/Ano],
       choose (@InicioDS, 'Segunda-feira', 'Terça-feira',
                          'Quarta-feira', 'Quinta-feira',
                          'Sexta-feira', 'Sábado', 'Domingo')
as [Semana inicia em], 
       convert (char(10), @DataInicial, 103) as [Data inicial], 
       convert (char(10), @DataFinal, 103) as [Data final];

Observe que no código sql otimizado foi invertida a sequência em que obtém as datas: passou a primeiro obter a data final da semana alvo e após a data inicial. Além disso, utilizou a função DATEADD para somar semanas diretamente, sem necessidade do laço que calculava dia a dia.

Também foi implementado no código sql a validação dos parâmetros. Agora é fácil converter o código sql em um procedimento (stored procedure) ou em uma função escalar.

O resultado do código sql #2.4 é o seguinte:

2.3. Soluções de terceiros

Após escrever o artigo resolvi pesquisar na web o que há de soluções propostas para o problema. A primeira delas, encontrada na página Transforming Week Number to Date (dd/MM) in SQL, é super compacta:

– Solução 1
declare @wk int  set @wk = 21
declare @yr int  set @yr = 2016
select dateadd (week, @wk-1, dateadd (year, @yr-1900, 0)) - 4 - datepart(dw, dateadd (week, @wk-1, dateadd (year, @yr-1900, 0)) - 4) + 1

Uma única fórmula? Parece mágica! Ao testá-la com os mesmos valores utilizados nos testes deste artigo, semana 9/2020, ela informa que o resultado é 17/2/2020, para semana iniciando na segunda-feira. Entretanto, o resultado correto é 24/2/2020. Ou seja, compacta mas inútil…

Passei então para a “solution 2” do mesmo artigo, que contém o seguinte código sql:

– Solução 2
declare @Week_Number int, @Year int, @Year_Start_Day date, @Week_Day date
select @Week_Number = 1, @Year = 2016

select @Year_Start_Day = cast(@Year as nvarchar(4)) + '0101'
select @Week_Day =  dateadd(wk, @Week_Number, @Year_Start_Day)
select dateadd(dd, 1 - datepart(weekday, @Week_Day), @Week_Day)

Alterei os valores de @Week_Number para 9 e de @Year para 2020, acrescentei a configuração de datefirst para segunda-feira e o resultado foi 2/3/2020. Ops! Totalmente errado… Descartei então esse artigo, mesmo tendo outras soluções nele propostas, porque foi possível perceber que dali não sairia nada de útil.

Passei os olhos em outros artigos e fóruns, fiz testes rápidos e vários deles apresentando resultados errados para determinadas combinações de semana e ano. Já estava desistindo até que me deparei com duas soluções que, aparentemente, são simples e – me parece – confiáveis.

Selecionei uma delas para apresentar aqui:

– Solução 3 (simplificada)
– fonte: https://stackoverflow.com/questions/607817/get-dates-from-a-week-number-in-t-sql

DECLARE @WeekNum INT, @YearNum char(4);
SELECT @WeekNum = 9, @YearNum = '2020';

SELECT DATEADD(wk, DATEDIFF(wk, 6, '1/1/' + @YearNum) + (@WeekNum-1), 6) as StartOfWeek,
DATEADD(wk, DATEDIFF(wk, 5, '1/1/' + @YearNum) + (@WeekNum-1), 5) as EndOfWeek;

Nessa solução, para calcular as datas inicial e final foi utilizado um artifício que é utilizado para truncar datas para expressões do tipo datetime. Por exemplo, se tenho o valor

23/2/2020 08:12:55.007

e desejo obter somente a data, uma forma antiga de se fazer isto é

dateadd (day, datediff (day, 0, @Data), 0)

e temos como resultado

23/2/2020 00:00:00.000

Experimente:

declare @Data datetime;
set @Data= convert (datetime, '23/2/2020 08:12:55.007', 103);
SELECT @Data, dateadd (day, datediff (day, 0, @Data), 0);

Esse mesmo artifício é utilizado para truncar milissegundos, segundos, minutos etc. E, pelo que percebi, nessa solução 3 foi utilizado esse artifício.

Não encontrei erros nos testes que fiz com a solução 3. Não foram testes exaustivos mas foram os mesmos que utilizei em outras soluções e que mostraram falhas nelas. Entretanto, esta solução 3 somente funciona para semanas que iniciam no domingo; caso se altere o datefirst para 1 (segunda-feira), por exemplo, a configuração é ignorada. A explicação está nas notas da resposta, no fórum onde obtive essa solução. Observe que na solução 3 não há tratamento para virada de ano, casos da primeira e última semana do ano.

3. ISO 8601

3.1. O que é (ou não é)

ISO é a sigla de International Organization for Standardization, ou Organização Internacional de Padronização, no português. A função da ISO é aprovar normas internacionais em um grande número de áreas de interesse econômico e técnico, como normas técnicas, classificações, normas de procedimento, etc. O correspondente aqui no Brasil é a ABNT – Associação Brasileira de Normas Técnicas.

A ISO mete o bedelho em tudo e também resolveu definir como devemos escrever datas e horas. Ao longo dos anos foi publicando várias normas até que em 1988 lançou a ISO 8601, substituindo diversas normas anteriores. Não contente, a ISO vem continuamente alterando essa norma, o que já ocorreu pelo menos 4 vezes, desde então.

Sobre a norma ISO 8601, it applies to these representations and formats: dates, in the Gregorian calendar (including the proleptic Gregorian calendar); times, based on the 24-hour timekeeping system, with optional UTC offset; time intervals; and combinations thereof.

No verbete ISO 8601, da wikipedia, encontram-se informações sobre a norma, como princípios gerais, datas, horas, intervalos de tempo. Não vou transcrever para cá o que já bem está detalhado em outro local; basta você clicar no link.

No Brasil, a padronização de datas e horas é pela norma NBR 5892. E, para variar, há pontos divergentes entre a norma internacional e a nacional e que afetam diretamente a nossa busca pelo primeiro dia da semana… É que pela ISO 8601 o primeiro dia da semana é a segunda-feira mas pela NBR 5892 no Brasil o primeiro dia da semana é o domingo. Pode isso?

No artigo (do sítio web Calendarr) Qual o primeiro dia da semana? há algumas explicações sobre porque no Brasil o domingo é considerado o primeiro dia da semana. Mas é somente a ABNT que pensa assim, pois para o brasileiro o final de semana incorpora sábado e domingo, o que significa que o início de semana é a segunda-feira: vox populi vox dei.

Retornando ao assunto deste artigo, qual o impacto da ISO 8601 nos bancos de dados e na definição de semanas? Enooorme!

No calendário gregoriano as semanas são numeradas por ano, conforme exemplificado no capítulo 2 deste artigo. Já pela ISO week date, o processo de numeração é um pouco mais complexo, sendo que a primeira regra é que toda semana começa sempre em uma Segunda-Feira e termina no Domingo.

Para determinar a primeira semana do ano, as regras são:

    • se o dia 1 de Janeiro cai na segunda, terça, quarta ou quinta-feira, então a semana em que está o dia 1 de Janeiro é a “Semana 1” e dela fazem parte os últimos dias do ano anterior que estejam na mesma semana; exceto, é claro, se o dia 1 de Janeiro cai em uma segunda-feira;
    • mas se o dia 1 de Janeiro cai na sexta-feira, sábado ou domingo, então o dia 1 de Janeiro é considerado parte da última semana do ano anterior e a “Semana 1” é a semana seguinte, iniciando na primeira segunda-feira logo após o dia 1 de Janeiro.

Complicado não? E tem regras também para determinar qual é a última semana de um ano…

3.2. Versão ISO 8601

Deixando a conceituação de lado, vamos ao objetivo deste artigo que é “a partir de semana/ano obter qual o período do ano referente à semana”. Na ISO 8601 uma das formas de representar a semana é aaaaWss, onde aaaa é o ano, seguindo da letra W e então do número da semana (2 algarismos). Por exemplo, a “Semana 1” do ano de 2024 é 2024W01. Há outras formas de representação, conforme consta no padrão ISO 8601. mas para este artigo vamos nos ater a somente esta.

O primeiro parâmetro da função DATEPART indica qual ação desejamos e já vimos exemplificação das ações week e weekday no capítulo 2. Agora vamos utilizar a ação iso_week.

-- código 3.1
-- Autor: José Diz
-- Publicado em: Porto SQL - https://portosql.wordpress.com

------ informe aqui o parâmetro de execução
declare @SemanaISO char(7);
set @SemanaISO= '2020W09';

------ analisa o parâmetro de execução
declare @Ano smallint, @Semana tinyint;

-- conteúdo
IF @SemanaISO is null 
   or len (ltrim (rtrim (@SemanaISO))) < 7
  begin
  PRINT 'Parâmetro incorreto. Formato é aaaaWss';
  return
  end;

IF substring (@SemanaISO, 5, 1) <> 'W'
  begin
  PRINT 'Parâmetro incorreto. Formato é aaaaWss';
  return
  end;

-- ano
set @Ano= try_cast (substring (@SemanaISO, 1, 4) as smallint);
IF @Ano is null or @Ano < 1583 or @Ano > 9998
  begin
  PRINT 'Valor de ano inválido';
  return
  end;

-- semana
set @Semana= try_cast (substring (@SemanaISO, 6, 2) as int);
IF @Semana is null or @Semana < 1 or @Semana > 53
  begin
  PRINT 'Valor de semana inválido';
  return
  end;

----- definição das datas inicial e final da semana/ano
declare @DataInicial date, @DataFinal date,
@PrimeiraSegunda date;

set datefirst 1; -- semana inicia sempre na segunda-feira

-- calcula segunda-feira da primeira semana do ano
set @PrimeiraSegunda= datefromparts (@Ano, 1, 1);
IF datepart (weekday, @PrimeiraSegunda) <= 4
  set @PrimeiraSegunda= dateadd (day, (1 - datepart (weekday, @PrimeiraSegunda)), @PrimeiraSegunda)
else 
  set @PrimeiraSegunda= dateadd (day, (8 - datepart (weekday, @PrimeiraSegunda)), @PrimeiraSegunda);

--
set @DataInicial= dateadd (week, @Semana -1, @PrimeiraSegunda);
set @DataFinal= dateadd (day, +6, @DataInicial);

----- apresentação do resultado
SELECT @SemanaISO as [Semana ISO],
       'Segunda-feira' as [Semana inicia em],
       convert (char(10), @DataInicial, 103) as [Data inicial], 
       convert (char(10), @DataFinal, 103) as [Data final];

No código #3.1 não foi feita a validação completa do parâmetro. Fica como exercício para você, que está lendo este artigo. Atento para as regras que definem a última semana do ano (52 ou 53).

4. Conclusão

O que era para ser um artigo curto acabou-se alongando mais do que o esperado. O primeiro motivo foi o item 1.2, em que foi explicado os cuidados que se deve ter no uso da função DATEPART. Já o segundo motivo foi a apresentação passo a passo da solução. Ou seja, este não é um artigo curto.

E então, sua semana começa no domingo ou na segunda-feira?


Há mais artigos sobre manipulação de data e hora, aqui no Porto SQL; clique em data&hora.

Deixe um comentário

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.