Compartilhar via


As funções Format ou DatePart podem devolver o número da semana errada para a última segunda-feira do ano

Aviso

Existe um problema com a utilização desta função. A última segunda-feira em alguns anos de calendário pode ser devolvida como semana 53 quando deve ser a semana 1. Para obter mais informações e uma solução alternativa, consulte As funções Format ou DatePart podem devolver o número da semana errada da última segunda-feira do ano.

Sintomas

Quando utiliza a função Format ou DatePart para determinar o número da semana para datas com a seguinte sintaxe:

  • Format(AnyDate, "ww", vbMonday, vbFirstFourDays)
  • DatePart("ww", AnyDate, vbMonday, vbFirstFourDays)

A última segunda-feira em alguns anos de calendário é devolvida como semana 53, quando deveria ser a semana 1.

Motivo

Ao determinar o número da semana de uma data de acordo com a norma ISO 8601, a chamada da função subjacente ao ficheiro de Oleaut32.dll devolve erroneamente a semana 53 em vez da semana 1 para a última segunda-feira em determinados anos.

Solução

Utilize uma função definida pelo utilizador para devolver o número semana com base nas regras da norma ISO 8601. Neste artigo, está incluído um exemplo.

Informações adicionais

A norma ISO 8601 é utilizada extensivamente na Europa e inclui o seguinte:

ISO 8601 "Data elements and interchange formats - Information interchange   - Representation of dates and times"
ISO 8601 : 1988 (E) paragraph 3.17:
"week, calendar: A seven day period within a calendar year, starting on a Monday and identified by its ordinal number within the year; the first calendar week of the year is the one that includes the first Thursday of that year. In the Gregorian calendar, this is equivalent to the week which includes 4 January."

Isto pode ser implementado ao aplicar estas regras para semanas de Calendário:

  • Um ano é dividido em 52 ou 53 semanas de calendário.
  • Uma semana de calendário tem sete dias. Segunda-feira é dia 1 e domingo é dia 7.
  • A primeira semana de calendário de um ano é a que contém, pelo menos, quatro dias.
  • Se um ano não for concluído num domingo, os seus 1-3 últimos dias pertencem à primeira semana de calendário do próximo ano ou os primeiros 1 a 3 dias do próximo ano pertencem à última semana de calendário do ano atual.
  • Apenas um ano de início ou conclusão numa quinta-feira tem 53 semanas de calendário.

No Visual Basic e no Visual Basic for Applications, todas as funcionalidades de data, exceto a função DateSerial, são provenientes de chamadas para o ficheiro Oleaut32.dll. Uma vez que as funções Format() e DatePart() podem devolver o número da semana do calendário de uma determinada data, ambas são afetadas por este erro. Para evitar este problema, tem de utilizar o código alternativo fornecido por este artigo.

Passos para Reproduzir Comportamento

  1. Abra o projeto do Visual Basic numa aplicação do Office (Alt + F11).

  2. No menu Projeto , adicione um novo módulo.

  3. Cole o seguinte código no módulo:

    Option Explicit
    
    Public Function Test1()
    ' This code tests a "problem" date and the days around it
    Dim DateValue As Date
    Dim i As Integer
    
    Debug.Print "   Format function:"
    DateValue = #12/27/2003#
    For i = 1 To 4   ' examine the last 4 days of the year
     DateValue = DateAdd("d", 1, DateValue)
     Debug.Print "Date: " & DateValue & "   Day: " & _
     Format(DateValue, "ddd") & "   Week: " & _
     Format(DateValue, "ww", vbMonday, vbFirstFourDays)
    Next i
    End Function
    
    Public Function Test2()
    ' This code lists all "Problem" dates within a specified range
     Dim MyDate As Date
     Dim Years As Long
     Dim days As Long
     Dim woy1 As Long
     Dim woy2 As Long
     Dim ToPrint As String
    
     For Years = 1850 To 2050
     For days = 0 To 3
     MyDate = DateSerial(Years, 12, 28 + days)
     woy1 = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
     woy2 = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
     If woy2 > 52 Then
     If Format(MyDate + 7, "ww", vbMonday, vbFirstFourDays) = 2 Then _
     woy2 = 1
     End If
     If woy1 <> woy2 Then
     ToPrint = MyDate & String(13 - Len(CStr(MyDate)), " ")
     ToPrint = ToPrint & Format(MyDate, "dddd") & _
     String(10 - Len(Format(MyDate, "dddd")), " ")
     ToPrint = ToPrint & woy1 & String(5 - Len(CStr(woy1)), " ")
     ToPrint = ToPrint & woy2
     Debug.Print ToPrint
     End If
     Next days
    Next Years
    End Function
    
  4. Utilize (Ctrl + G) para abrir a Janela Imediata se ainda não estiver aberta.

  5. Escreva ? Teste1 na janela Imediato e prima Enter. Tenha em atenção os seguintes resultados na janela Imediato:

    Format function:
    Date: 12/28/03   Day: Sun   Week: 52
    Date: 12/29/03   Day: Mon   Week: 53
    Date: 12/30/03   Day: Tue   Week: 1
    Date: 12/31/03   Day: Wed   Week: 1
    

    Com este formato, todas as semanas começam com segunda-feira, para que 29 de dezembro de 2003 seja considerado o início da Semana 1 e não parte da Semana 53.

  6. Escreva ? Teste2 na janela Imediato e prima Enter para ver uma lista de datas no intervalo especificado que detetam este problema. A lista inclui a data, Dia da semana (sempre segunda-feira), a Semana # devolvida pelo Formato (53) e o número da Semana que deve devolver (1.) Por exemplo:

    12/29/1851   Monday    53   1
    12/31/1855   Monday    53   1
    12/30/1867   Monday    53   1
    12/29/1879   Monday    53   1
    12/31/1883   Monday    53   1
    12/30/1895   Monday    53   1
    ...
    

Soluções alternativas

Se utilizar as funções Formatar ou DatePart, tem de verificar o valor devolvido. Quando tiver 53 anos, execute outra verificação e force uma devolução de 1, se necessário. Este exemplo de código demonstra uma forma de o fazer:

Function WOY (MyDate As Date) As Integer   ' Week Of Year
  WOY = Format(MyDate, "ww", vbMonday, vbFirstFourDays)
  If WOY > 52 Then
    If Format(MyDate + 7, "ww", vbMonday, vbFirstFourDays) = 2 Then WOY = 1
  End If
End Function

Pode evitar utilizar estas funções para determinar o número da Semana ao escrever código que implementa as regras ISO 8601 descritas acima. O exemplo seguinte demonstra uma função de substituição para devolver o número Semana.

Exemplo Passo a Passo

  1. Abra o projeto do Visual Basic numa aplicação do Office (Alt + F11).

  2. No menu Projeto , adicione um novo módulo.

  3. Cole o seguinte código no módulo:

    Option Explicit
    
    Function WeekNumber(InDate As Date) As Integer
     Dim DayNo As Integer
     Dim StartDays As Integer
     Dim StopDays As Integer
     Dim StartDay As Integer
     Dim StopDay As Integer
     Dim VNumber As Integer
     Dim ThurFlag As Boolean
    
     DayNo = Days(InDate)
     StartDay = Weekday(DateSerial(Year(InDate), 1, 1)) - 1
     StopDay = Weekday(DateSerial(Year(InDate), 12, 31)) - 1
     ' Number of days belonging to first calendar week
     StartDays = 7 - (StartDay - 1)
     ' Number of days belonging to last calendar week
     StopDays = 7 - (StopDay - 1)
     ' Test to see if the year will have 53 weeks or not
     If StartDay = 4 Or StopDay = 4 Then ThurFlag = True Else ThurFlag = False
     VNumber = (DayNo - StartDays - 4) / 7
     ' If first week has 4 or more days, it will be calendar week 1
     ' If first week has less than 4 days, it will belong to last year's
     ' last calendar week
     If StartDays >= 4 Then 
     WeekNumber = Fix(VNumber) + 2 
     Else 
     WeekNumber = Fix(VNumber) + 1
     End If
     ' Handle years whose last days will belong to coming year's first
     ' calendar week
     If WeekNumber > 52 And ThurFlag = False Then WeekNumber = 1
     ' Handle years whose first days will belong to the last year's 
     ' last calendar week
     If WeekNumber = 0 Then
     WeekNumber = WeekNumber(DateSerial(Year(InDate) - 1, 12, 31))
     End If
    End Function
    
    Function Days(DayNo As Date) As Integer
     Days = DayNo - DateSerial(Year(DayNo), 1, 0)
    End Function
    
    Public Function Test3()
     Dim DateValue As Date, i As Integer
    
     Debug.Print "   WeekNumber function:"
     DateValue = #12/27/2003#
     For i = 1 To 4   ' examine the last 4 days of the year
     DateValue = DateAdd("d", 1, DateValue)
     Debug.Print "Date: " & DateValue & "   Day: " & _
          Format(DateValue, "ddd") & "   Week: " & WeekNumber(DateValue)
     Next i
    End Function
    
  4. Utilize (Ctrl + G) para abrir a Janela Imediata se ainda não estiver aberta.

  5. Escreva ? Teste3 na janela Imediato e prima Enter. Tenha em atenção os seguintes resultados na janela Imediato:

    WeekNumber function:
    Date: 12/28/03   Day: Sun   Week: 52
    Date: 12/29/03   Day: Mon   Week: 1
    Date: 12/30/03   Day: Tue   Week: 1
    Date: 12/31/03   Day: Wed   Week: 1
    

    Segunda-feira é considerado a Semana 1 como deveria ser.