Format- oder DatePart-Funktionen können eine falsche Wochennummer für den letzten Montag im Jahr zurückgeben.

Warnung

Bei der Verwendung dieser Funktion liegt ein Problem vor. Der letzte Montag in einigen Kalenderjahren kann als Woche 53 zurückgegeben werden, wenn es Woche 1 sein sollte. Weitere Informationen und eine Problemumgehung finden Sie unter Format oder DatePart-Funktionen können eine falsche Wochennummer für den letzten Montag im Jahr zurückgeben.

Symptome

Wenn Sie entweder die Format- oder datePart-Funktion verwenden, um die Wochenzahl für Datumsangaben mit der folgenden Syntax zu bestimmen:

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

der letzte Montag in einigen Kalenderjahren wird als Woche 53 zurückgegeben, wenn es Woche 1 sein sollte.

Ursache

Bei der Bestimmung der Wochenzahl eines Datums gemäß dem ISO 8601-Standard gibt der zugrunde liegende Funktionsaufruf an die Oleaut32.dll Datei versehentlich Woche 53 anstelle von Woche 1 für den letzten Montag in bestimmten Jahren zurück.

Lösung

Verwenden Sie eine benutzerdefinierte Funktion, um die Wochenzahl basierend auf den Regeln für den ISO 8601-Standard zurückzugeben. In diesem Artikel ist ein Beispiel enthalten.

Weitere Informationen

Die Norm ISO 8601 wird in Europa umfassend verwendet und umfasst Folgendes:

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

Dies kann durch Anwenden der folgenden Regeln für Kalenderwochen implementiert werden:

  • Ein Jahr ist in 52 oder 53 Kalenderwochen unterteilt.
  • Eine Kalenderwoche hat 7 Tage. Montag ist Tag 1, Sonntag ist Tag 7.
  • Die erste Kalenderwoche eines Jahres ist die Kalenderwoche, die mindestens 4 Tage enthält.
  • Wenn ein Jahr nicht an einem Sonntag abgeschlossen wird, gehören entweder seine letzten 1-3 Tage zur ersten Kalenderwoche des nächsten Jahres oder die ersten 1-3 Tage des nächsten Jahres gehören zur letzten Kalenderwoche des aktuellen Jahres.
  • Nur ein Jahr, das an einem Donnerstag beginnt oder endet, hat 53 Kalenderwochen.

In Visual Basic und Visual Basic for Applications stammen alle Datumsfunktionen mit Ausnahme der DateSerial-Funktion aus Aufrufen der Oleaut32.dll-Datei. Da sowohl die Format()- als auch die DatePart()-Funktion die Kalenderwochennummer für ein bestimmtes Datum zurückgeben können, sind beide von diesem Fehler betroffen. Um dieses Problem zu vermeiden, müssen Sie den in diesem Artikel bereitgestellten alternativen Code verwenden.

Schritte zum Reproduzieren des Verhaltens

  1. Öffnen Sie das Visual Basic-Projekt in einer Office-Anwendung (ALT +F11).

  2. Fügen Sie im Menü Projekt ein neues Modul hinzu.

  3. Fügen Sie den folgenden Code in das Modul ein:

    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. Verwenden Sie (STRG+G), um das Direktfenster zu öffnen, wenn es noch nicht geöffnet ist.

  5. Geben Sie ein? Testen Sie im Direktfenster, und drücken Sie die EINGABETASTE. Notieren Sie sich die folgenden Ergebnisse im Direktfenster:

    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
    

    Bei diesem Format beginnen alle Wochen mit Montag, sodass der 29.12.2003 als Beginn von Woche 1 und nicht als Teil von Woche 53 betrachtet werden sollte.

  6. Geben Sie ein? Testen Sie2 im Direktfenster, und drücken Sie die EINGABETASTE, um eine Liste der Datumsangaben im angegebenen Bereich anzuzeigen, bei denen dieses Problem auftritt. Die Liste enthält das Datum, den Wochentag (immer Montag), die von Format (53) zurückgegebene Woche #und die Wochennummer, die zurückgegeben werden soll (1.). Zum Beispiel:

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

Problemumgehungen

Wenn Sie die Funktionen Format oder DatePart verwenden, müssen Sie den Rückgabewert überprüfen. Wenn es 53 ist, führen Sie eine weitere Überprüfung aus, und erzwingen Sie bei Bedarf die Rückgabe von 1. In diesem Codebeispiel wird eine Möglichkeit veranschaulicht, dies zu tun:

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

Sie können die Verwendung dieser Funktionen vermeiden, um die Wochenzahl zu bestimmen, indem Sie Code schreiben, der die oben beschriebenen ISO 8601-Regeln implementiert. Im folgenden Beispiel wird eine Ersetzungsfunktion zum Zurückgeben der Wochennummer veranschaulicht.

Schritt-für-Schritt-Beispiel

  1. Öffnen Sie das Visual Basic-Projekt in einer Office-Anwendung (ALT +F11).

  2. Fügen Sie im Menü Projekt ein neues Modul hinzu.

  3. Fügen Sie den folgenden Code in das Modul ein:

    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. Verwenden Sie (STRG+G), um das Direktfenster zu öffnen, wenn es noch nicht geöffnet ist.

  5. Geben Sie ein? Testen Sie im Direktfenster, und drücken Sie die EINGABETASTE. Notieren Sie sich die folgenden Ergebnisse im Direktfenster:

    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
    

    Beachten Sie, dass Montag als Woche 1 betrachtet wird, wie er sein sollte.