警告
使用此函式時發生問題。 在某些日曆年份中,最後的星期一可能被算作第 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 的影響。 若要避免這個問題,您必須使用本文提供的替代程序代碼。
重現行為的步驟
在 Office 應用程式內開啟 Visual Basic 專案 (Alt + F11)。
從 [專案] 功能表中,新增模組。
將下列程式代碼貼到模組中:
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如果尚未開啟,請使用 [Ctrl + G] 來開啟 [即時視窗]。
輸入 ? 在 [實時運算] 視窗中測試 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 周的一部分。
輸入 ? [實時運算] 視窗中的 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 規則的程式代碼,以避免使用這些函式來判斷周數。 下列範例示範替代函式,以傳回週數。
逐步示例
在 Office 應用程式內開啟 Visual Basic 專案 (Alt + F11)。
從 [專案] 功能表中,新增模組。
將下列程式代碼貼到模組中:
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如果尚未開啟 [即時運算視窗],請使用 [Ctrl + G] 來開啟。
在 [即時視窗] 中輸入 ?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 周,因為它應該是這樣的。