Funktionerna Format eller DatumDel kan returnera fel veckonummer för den senaste måndagen i år

Anteckning

Office 365 ProPlus byter namn till Microsoft 365-appar för företag. Mer information om den här ändringen finns i det här blogginlägget.

Varning

Det finns ett problem med användningen av den här funktionen. Den sista måndagen under vissa kalenderår kan returneras som vecka 53 när det ska vara vecka 1. Mer information och en lösning finns i Funktionerna Format och DatumDel kan returnera fel veckonummer för förra måndagen i år.

Symptom

När du använder funktionen Format eller DatumDel för att fastställa veckonumret för datum med följande syntax:

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

Den sista måndagen under vissa kalenderår returneras som vecka 53 när det ska vara vecka 1.

Orsak

När veckonumret för ett datum enligt ISO 8601-standarden fastställas returnerar det underliggande funktionsanropet till Oleaut32.dll-filen felaktigt vecka 53 i stället för vecka 1 för sista måndagen under vissa år.

Lösning

Använd en användardefinierad funktion för att returnera veckonumret enligt reglerna för ISO 8601-standarden. Ett exempel finns i den här artikeln.

Mer information

ISO 8601-standarden används mycket i Europa och omfattar följande:

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."

Det här kan implementeras genom att tillämpa dessa regler för kalenderveckor:

  • Ett år delas upp i antingen 52 eller 53 kalenderveckor.
  • En kalendervecka har 7 dagar. Måndag är dag 1, söndag är dag 7.
  • Den första kalenderveckan på ett år är den som innehåller minst 4 dagar.
  • Om ett år inte är avslutat på en söndag tillhör dess 1–3 sista dagar nästa års första kalendervecka eller de första 1–3 dagarna nästa år tillhör nu årets sista kalendervecka.
  • Endast ett år från och med torsdagen har 53 kalenderveckor.

I Visual Basic och Visual Basic for Applications kommer alla datumfunktioner, förutom funktionen DatumSerial, från anrop till Oleaut32.dll filen. Eftersom både funktionerna Format() och DatumDel() kan returnera kalenderns veckonummer för ett visst datum påverkas båda av den här buggen. Du måste använda den alternativa kod som finns i den här artikeln för att undvika det här problemet.

Steg för att återskapa beteende

  1. Öppna Visual Basic projekt i ett Office (Alt + F11).

  2. På menyn Project du till en ny modul.

  3. Klistra in följande kod i modulen:

    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. Använd (Ctrl + G) för att öppna direktfönstret om det inte redan är öppet.

  5. Skriv ? Testa1 i direktfönstret och tryck på Retur. Observera följande resultat i direktfönstret:

    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
    

    Med det här formatet börjar alla veckor med måndag så att 2003-12-29 ska ses som början av Vecka 1 och inte en del av Vecka 53.

  6. Skriv ? Testa2 i direktfönstret och tryck på Retur för att visa en lista med datum i det angivna intervallet där det här problemet uppstår. Listan innehåller datum, Vecka dag (alltid Måndag), Vecka # som returneras av Format (53) och veckonumret som ska returneras (1.) Till exempel:

    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
    ...
    

Lösningar

Om du använder funktionerna Format eller DatumDel måste du kontrollera returvärdet. När det är 53 kör du en ny kontroll och tvingar fram en retur av 1, om det behövs. Det här kodprovet visar ett sätt att göra detta:

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

Du kan undvika att använda dessa funktioner för att fastställa veckonummer genom att skriva kod som implementerar ISO 8601-reglerna som beskrivs ovan. I följande exempel visas en ersättningsfunktion för att returnera veckonumret.

Exempel med steg för steg

  1. Öppna Visual Basic projekt i ett Office (Alt + F11).

  2. På menyn Project du till en ny modul.

  3. Klistra in följande kod i modulen:

    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. Använd (Ctrl + G) för att öppna direktfönstret om det inte redan är öppet.

  5. Skriv ? Testa3 i direktfönstret och tryck på Retur. Observera följande resultat i direktfönstret:

    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
    

    Observera att måndag anses vara Vecka 1 som den ska.