Warnung
この関数の使用に問題があります。 一部の暦年で、最後の月曜日が本来週 1 であるべきところを週 53 として返されることがあります。 詳細と回避策については、「 Format 関数または DatePart 関数は、年の最後の月曜日に間違った週番号を返す可能性があります」を参照してください。
症状
Format 関数または DatePart 関数を使用して、次の構文を使用して日付の週番号を決定する場合:
Format(AnyDate, "ww", vbMonday, vbFirstFourDays)
DatePart("ww", AnyDate, vbMonday, vbFirstFourDays)
一部の暦年の最後の月曜日は、本来週 1 として返されるべきところ、週 53 として返されます。
原因
ISO 8601 標準に従って日付の週番号を決定すると、Oleaut32.dll ファイルの基になる関数呼び出しは、特定の年の最後の月曜日の週 1 ではなく、誤って第 53 週を返します。
解決策
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."
カレンダー週間に次のルールを適用することで実装できます。
- 1 年は 52 または 53 カレンダー週間に分けられます。
- カレンダーの週には 7 日間があります。 月曜日は 1 日目、日曜日は 7 日目です。
- 1 年の最初のカレンダー週は、少なくとも 4 日間を含む週です。
- 1 年が日曜日に終わっていない場合、その 1 から 3 の最後の日は来年の最初のカレンダー週に属するか、または来年の最初の 1 から 3 日は、現在の年の最後のカレンダー週に属します。
- 木曜日から始まるか終わるのは 1 年だけで、カレンダー週数は 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) キーを押して開きます。
イミディエイトウィンドウで ?Test1 を入力し、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 日は第 53 週の一部ではなく第 1 週の開始と見なされます。
?Test2 を即時ウィンドウに入力し、Enterキーを押すと、この問題が発生する指定範囲内の日付の一覧が表示されます。 この一覧には、日付、曜日 (常に月曜日)、書式 (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 を返します。 このコード サンプルでは、これを行う 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 週と見なされるべきものです。