TL;DR —
Year(d)、Month(d)、Day(d)、Hour(t)、Minute(t)、Second(t)は、日付から整数を 1 つずつ返します — 単純で曖昧さもありません。罠はWeekday(d)です。1〜7 を返しますが、その番号 付けは既定でvbSunday(1)になる第 2 引数に左右されるので、既定では月曜日は 1 ではなく 2 です。vbMondayを渡す(またはvb*定数と比較する)と、週末ロジックは静かに間違うのをやめます。
Sub PartsDemo()
Dim d As Date: d = #2026-08-26# ' 水曜日
Debug.Print Year(d), Month(d), Day(d) ' 2026 8 26
Debug.Print Weekday(d) ' 4 — 既定は日曜=1 なので水曜=4
Debug.Print Weekday(d, vbMonday) ' 3 — 月曜=1 なので水曜=3
Debug.Print DatePart("ww", d) ' 35 — 週番号(専用関数はない)
End Sub
部品を取り出すのは、部品から日付を組み立てることの逆向きです。Double は
手元にあり、そこから年、月、曜日がほしい。関数のほとんどは、あるべきとおりに退屈です — Year、
Month、Day はただ数値を読むだけ。Weekday は、必要以上に多くのマクロを終わらせてきた一つで、
その原因はまるごと、誰も覚えていない既定値にあります。
この記事で学べること
- 単純な取り出し役 —
Year、Month、Day、Hour、Minute、Second— それぞれ整数を 1 つ Weekdayが既定で月曜日に 2 を返す理由と、それを直すただ一つの引数- ひそかに間違っていない週末・平日の判定の書き方
DatePart— 週番号や四半期に届く汎用の取り出し役DatePart("ww", …)の週番号が年境界で 1 つずれることがある理由(ISO 8601)WeekdayNameとMonthNameはローカライズされた名前を返す — 表示に使い、解析には使わない
単純な取り出し役:それぞれ整数を 1 つ
Year、Month、Day は日付をカレンダーの構成要素に分け、Hour、Minute、Second は時刻の小数を
分けます。それぞれがただの Integer を返し、つまずくところはありません。
Dim d As Date: d = Now ' 2026-08-26 14:30:07
Debug.Print Year(d) ' 2026
Debug.Print Month(d) ' 8 (1〜12 の数値であって、名前ではない)
Debug.Print Day(d) ' 26
Debug.Print Hour(d) ' 14 (24 時間制)
Debug.Print Minute(d) ' 30
Debug.Print Second(d) ' 7
Month は "August" ではなく 8 を返します — 数値です。これらは、関連する日付(月初、翌年の
同じ日など)を組み立てるために DateSerial へ渡し戻す積み木です。ここに
驚きはありません — だからこそ Weekday が人の不意を突くのです。
Weekday が月曜日に 2 を返す理由
Weekday(date, [firstDayOfWeek]) は 1 から 7 の数値を返します — が、どの曜日が 1 かは第 2 引数で
決まり、省略すると VBA は vbSunday を使うので、日曜日 = 1、月曜日 = 2 になります。
Weekday(#2026-08-24#) ' 2 — その月曜日、既定で日曜=1 だから
Weekday(#2026-08-24#, vbMonday) ' 1 — これで月曜=1 … 日曜=7
これが一族全体の目玉のバグです。If Weekday(d) = 1 Then ' 月曜日 のようなコードは誤りで — 既定の
呼び出しでは 1 は日曜日です — If Weekday(d) = 6 Or Weekday(d) = 7 Then ' 週末 のようなコードは、
呼び出し箇所からは見えない「最初の曜日」の思い込みに丸ごと依存しています。堅牢な直し方は 2 つあり、
どちらも当て推量を取り除きます。
' 直し方 1 — 最初の曜日を明示すれば、月曜が本当に 1 になる:
If Weekday(d, vbMonday) >= 6 Then MsgBox "weekend" ' 6 = 土, 7 = 日
' 直し方 2 — マジックナンバーではなく、名前付き定数と比較する:
If Weekday(d) = vbSaturday Or Weekday(d) = vbSunday Then MsgBox "weekend"
vb* 定数(vbSunday、vbMonday、… vbSaturday)は、最初の曜日の引数に関わらず正しいので、
それらと比較するのが最も安全な習慣です。ルールはこうです。曜日番号を裸の数値で書かないこと —
vbMonday を渡して番号付けを期待どおりにするか、vb* 定数と比較しましょう。
DatePart:汎用の取り出し役
DatePart(interval, date, [firstDayOfWeek], [firstWeekOfYear]) は万能ナイフ版です。
DateAdd や DateDiff と同じインターバルコードを取り、その単位を
取り出します。年・月・日・時刻については専用関数と同じですが、自分専用の関数を持たないいくつかの
部品に届く唯一の組み込み手段でもあります。
DatePart("q", d) ' 年の四半期 (1〜4) — Quarter() 関数は存在しない
DatePart("ww", d) ' 年の週番号 (1〜53)
DatePart("y", d) ' 年内通算日 (1〜366) — 「年内通算日」を意味する "y"
DatePart("m", d) ' 8 — Month(d) と同じ
四半期や週番号が必要なときは DatePart に手を伸ばし、日常的な部品には素の Year / Month
/ Day を使いましょう。そのほうが読みやすいからです。DateAdd と同じコードの罠に注意 — "y" は
年内通算日、"ww" は週です。一度声に出しておきましょう。
年境界での週番号の罠
DatePart("ww", …) には、人を驚かせる独自の既定値があります。年の最初の週と週の最初の曜日が、
どちらも US 式のルール(vbSunday、vbFirstJan1)を既定とし、これはISO 8601 ではありません —
ヨーロッパの大半と多くの業務カレンダーが使う標準です。そのため 12 月末や 1 月初めの日が、思って
いない週番号に着地することがあります。
DatePart("ww", #2026-12-31#) ' 53 (US 式の既定ルール)
DatePart("ww", #2026-12-31#, vbMonday, vbFirstFourDays) ' ISO 式の答え
週番号が重要なら — 給与計算の週、スプリント番号、壁のカレンダーと一致しなければならない何か —
最初の曜日に vbMonday を、最初の週のルールに vbFirstFourDays を渡して ISO 8601 に合わせましょう。
「年末年始のあたりで週番号が 1 つずれる」という定番のバグは、いつもこの 2 つの既定値です。
WeekdayName と MonthName はローカライズされる
曜日や月の番号を名前に変えるには、WeekdayName と MonthName を使います。どちらもマシンの
言語で名前を返すので、表示には理想的で、読み戻す用途には誤りです。
WeekdayName(2) ' 英語環境で "Monday"、ドイツ語環境で "Montag"
MonthName(8) ' ロケール次第で "August" / "August" / "août"
WeekdayName(2, True) ' 省略形: "Mon"
出力がユーザーのロケールに従うので、固定の英語文字列と比較したり、別のシステムが読むファイルに
書き込んだりしてはいけません — それはロケールバグの種です。自分で制御できる安定したラベルには、
代わりに日付を書式化しましょう。Format(d, "dddd") は完全な曜日名を、Format(d, "mmmm") は月名を
返します。これも Format 経由でロケールを意識しますが、明示的です。
WeekdayName / MonthName は画面表示に、ロジックには数値(Weekday、Month)を使いましょう。
率直な結論:部品は簡単、既定値がやっかい
日付から部品を取り出すのは、VBA の日付まわりで最も不思議のないことです — ただし、不釣り合いに多くの バグを生む注釈が 1 つあります。
- 単純な部品、単純な関数 →
Year、Month、Day、Hour、Minute、Second。それぞれ整数を 返し、Monthは名前ではなく数値です。 Weekdayには隠れた既定値 → それはvbSundayなので、月曜日は 2 です。vbMondayを 渡すか、vbSaturday/vbSundayと比較すること — 裸の数値は禁物。- 関数のない部品には
DatePart→ 四半期("q")と週番号("ww")。ISO 週はvbMondayvbFirstFourDaysで合わせます。
- 名前はローカライズされる →
WeekdayName/MonthNameはマシンの言語に従う。表示に使い、 決して解析には使わないこと。
最も多くのバグを防ぐたった一つの習慣。曜日番号を決してリテラルにしないこと。どの曜日が最初かを 言うか、意味する曜日を名前で指すか、です。
ExcelMaster の活用
部品取り出しのバグが厄介なのは、コードが見るからに正しそうに見えるからです。Weekday = 1 を
チェックする週末フィルター、毎年 1 月に給与カレンダーと食い違う週番号の列、別の言語のシステムが
読めない WeekdayName をファイルに書くレポート。どれも、誰も覚えていなかった既定値です。
ExcelMaster は、最初から正しい
取り出しを書きます。ルールを説明すれば — 「週末を飛ばす」、「四半期で行をまとめる」、「各行に ISO
週のラベルを付ける」 — 曜日ロジックには Weekday(d, vbMonday) か vb* 定数を、週番号には ISO 引数
付きの DatePart を使い、表示用の名前と分岐に使う数値を分けて保ちます。あなたはカレンダーのルールを
説明するだけ。隠れた既定値に頼らない部品取り出しを書くのは、それがやります。
よくある質問
VBA で Weekday が間違った曜日番号を返すのはなぜ?
Weekday が週の番号付けを第 2 引数で決め、それが既定で vbSunday になるからです — つまり
日曜日が 1、月曜日が 2 です。月曜日が 1 だと思い込んでいると、すべての結果が 1 つずれます。最初の
曜日を明示して Weekday(d, vbMonday) とすれば月曜日が 1、日曜日が 7 になり、あるいは結果を名前付き
定数 vbSaturday と vbSunday と比較しましょう。ハードコードした数値ではなく。
VBA で日付が週末かどうかを判定するには?
最初の曜日の既定値に噛まれないよう、定数を使います — If Weekday(d) = vbSaturday Or Weekday(d) = vbSunday Then …。vb* 定数は、どんな「最初の曜日」引数が働いていても正しいままです。あるいは
Weekday(d, vbMonday) >= 6 は 6(土曜)と 7(日曜)を週末として扱います — ただし vbMonday を
渡したことを確かめてください。
VBA で日付の四半期や週番号を取得するには?
専用の Quarter や Week 関数がないので、DatePart を使います。DatePart("q", d) は四半期 1〜4 を、
DatePart("ww", d) は週番号を返します。ISO 8601 の週番号には追加の引数を渡します —
DatePart("ww", d, vbMonday, vbFirstFourDays)。さもないと年末年始の週が 1 つずれることがあります。
VBA の Month と MonthName の違いは?
Month(d) は月を数値(1〜12)で返し、ロジックに使います。MonthName(n) は月番号を名前の
テキストに変えます(MonthName(8) は "August")。MonthName はマシンの言語に従うので、表示専用に
しましょう。分岐や比較には Month の数値を使います。
VBA の WeekdayName は英語名を返しますか?
英語環境でのみです。WeekdayName と MonthName はシステムのロケールで名前を返すので、同じ
コードでもマシンによって "Monday" や "Montag" や "lundi" になります。その出力を固定の英語文字列と
比較してはいけません。制御されたラベルには Format(d, "dddd") を、ロジックには数値の Weekday を
使いましょう。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-26。
関連ガイド: VBA DateAdd・DateDiff — 日付計算 · VBA Now・Date・Time — 時計を読む · VBA Format — 日付・数値の書式設定 · VBA CStr・CDate・Val — 文字列を数値・日付へ変換 · VBA For ループ — 実務例
