共用方式為


Format 或 DatePart 函式可能會傳回年度中的最後一個星期一的錯誤周數

警告

使用此函式時發生問題。 在某些日曆年份中,最後的星期一可能被算作第 53 周,而實際上它應該是第 1 周。 如需詳細資訊和因應措施,請參閱 Format 或 DatePart 函式可能會在年度中最後一個週一回傳錯誤的周數

癥狀

當您使用 Format 或 DatePart 函式,並使用下列語法來判斷日期的周數時:

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

某些日曆年度的最後一個星期一會被錯誤地標示為第 53 周,實際上應為第 1 周。

原因

根據 ISO 8601 標準判斷日期的周數時,Oleaut32.dll 檔案的底層函數呼叫錯誤地傳回第 53 周,而不是特定年份最後一個星期一的第 1 周。

解決辦法

使用使用者定義的函式,根據 ISO 8601 標準的規則傳回 Week 號碼。 本文包含範例。

詳細資訊

ISO 8601 標準在歐洲廣泛使用,包括:

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

您可以針對行事曆周套用這些規則來實作:

  • 一年分為 52 或 53 個日曆周。
  • 日曆周有七天。 星期一為第 1 天,星期日為第 7 天。
  • 一年的第一個日曆周是包含至少四天的日曆周。
  • 如果一年不是在星期日結束,它的最後 1-3 天屬於明年的第一個日曆周,或明年的前 1-3 天屬於目前年份的最後一個日曆周。
  • 只有一年開始於星期四或結束於星期四,才會有 53 個日曆週。

在 Visual Basic 和 Visual Basic for Applications 中,除了 DateSerial 函式之外,所有日期功能都是來自對 Oleaut32.dll 檔案的呼叫。 因為 Format() 和 DatePart() 函式都可以傳回指定日期的行事歷周號碼,因此兩者都會受到這個 Bug 的影響。 若要避免這個問題,您必須使用本文提供的替代程序代碼。

重現行為的步驟

  1. 在 Office 應用程式內開啟 Visual Basic 專案 (Alt + F11)。

  2. [專案] 功能表中,新增模組。

  3. 將下列程式代碼貼到模組中:

    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. 如果尚未開啟,請使用 [Ctrl + G] 來開啟 [即時視窗]。

  5. 輸入 在 [實時運算] 視窗中測試 1,然後按 Enter 鍵,記下 [實時運算] 視窗中的下列結果:

    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
    

    使用此格式,所有星期從星期一開始,因此 2003 年 12 月 29 日應該被視為第 1 周的開始,而不是第 53 周的一部分。

  6. 輸入 [實時運算] 視窗中的 Test2,然後按 Enter 以查看遇到此問題之指定範圍內的日期清單。 此清單包含日期、星期(一律為星期一)、格式傳回的週數 (53),以及應返回的週數 (1)。例如:

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

因應措施

如果您使用 Format 或 DatePart 函式,則需要檢查傳回值。 當它是 53 時,請執行另一個檢查,必要時強制返回 1。 此程式代碼範例示範執行此動作的其中一種方式:

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

您可以撰寫實作上述 ISO 8601 規則的程式代碼,以避免使用這些函式來判斷周數。 下列範例示範替代函式,以傳回週數。

逐步示例

  1. 在 Office 應用程式內開啟 Visual Basic 專案 (Alt + F11)。

  2. [專案] 功能表中,新增模組。

  3. 將下列程式代碼貼到模組中:

    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. 如果尚未開啟 [即時運算視窗],請使用 [Ctrl + G] 來開啟。

  5. 在 [即時視窗] 中輸入 ?Test3 並按 Enter,注意以下結果出現在 [即時視窗] 中:

    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
    

    星期一被認為是第 1 周,因為它應該是這樣的。