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標準的規則傳回周數。 本文包含一個範例。
其他相關資訊
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 () 函式都可以傳回指定日期的行事歷周數,因此這兩者都會受到此錯誤影響。 若要避免這個問題,您必須使用本文提供的替代程序代碼。
重現行為的步驟
在 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 周的一部分。
輸入 ?在 [即時運算] 視窗中測試 2,然後按 Enter 以查看發生此問題的指定範圍內的日期清單。 此列表包含日期、周日 (永遠是星期一) 、Format (53) 傳回的 Week #,以及它應該傳回的周數 (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 規則的程式代碼,以避免使用這些函式來判斷周數。 下列範例示範用來傳回 Week 編號的取代函式。
逐步範例
在 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) 來開啟 [即時運算視窗]。
輸入 ?在 [實時運算] 視窗中測試 3,然後按 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 周,因為它應該是。