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.