メインコンテンツまでスキップ

Appendix B. Day of the Week (曜日)

この付録では、任意のグレゴリオ暦の日付から曜日を計算する方法を示します。これは、RFC 3339が曜日情報を含まない理由を理解するために重要です—なぜなら、正確に計算できるからです。

Zeller's Congruence (ツェラーの公式)

曜日を計算するために一般的に使用されるアルゴリズムは、1882年にChristian Zellerによって発明されたZeller's Congruence(ツェラーの合同式)です。

公式

h = (q + ⌊13(m+1)/5⌋ + K + ⌊K/4⌋ + ⌊J/4⌋ - 2J) mod 7

ここで:

  • h: 曜日 (0 = 土曜日, 1 = 日曜日, 2 = 月曜日, ..., 6 = 金曜日)
  • q: 月の日 (1-31)
  • m: 月 (3-14、3 = 3月, 4 = 4月, ..., 12 = 12月, 13 = 1月, 14 = 2月)
  • K: 世紀内の年 (year % 100)
  • J: 世紀 (⌊year/100⌋)
  • ⌊x⌋: 床関数

注記: 1月と2月は前年の13番目と14番目の月として扱われます。

Python実装

def day_of_week_zeller(year, month, day):
"""
ツェラーの公式を使用して曜日を計算
戻り値: 0=土曜日, 1=日曜日, ..., 6=金曜日
"""
# 1月と2月は前年の13月と14月として扱う
if month < 3:
month += 12
year -= 1

q = day
m = month
K = year % 100
J = year // 100

h = (q + (13 * (m + 1)) // 5 + K + K // 4 + J // 4 - 2 * J) % 7

# 一般的な形式に変換: 0=月曜日, ..., 6=日曜日
# Zeller: 0=土, 1=日, 2=月, 3=火, 4=水, 5=木, 6=金
# 調整: 0=月, 1=火, 2=水, 3=木, 4=金, 5=土, 6=日
return (h + 5) % 7

def day_name(year, month, day):
"""曜日の名前を返す"""
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
return days[day_of_week_zeller(year, month, day)]

# 例
print(day_name(2002, 7, 15)) # Monday
print(day_name(2000, 1, 1)) # Saturday
print(day_name(1999, 12, 31)) # Friday

より簡単なアルゴリズム

プログラミング実装の場合、より直感的なアルゴリズムを使用できます:

def day_of_week_simple(year, month, day):
"""
簡略化された曜日計算
戻り値: 0=月曜日, ..., 6=日曜日
"""
# 各月前の累積日数(平年)
t = [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4]

if month < 3:
year -= 1

y = year % 100
c = year // 100

return (y + y // 4 + c // 4 - 2 * c + t[month - 1] + day) % 7

JavaScript実装

function dayOfWeek(year, month, day) {
// JavaScript Dateオブジェクトが自動的に曜日を計算
const date = new Date(year, month - 1, day);
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday'];
return days[date.getDay()];
}

// 例
console.log(dayOfWeek(2002, 7, 15)); // Monday

検証例

日付曜日検証済み
2002-07-15月曜日 (Monday)
2000-01-01土曜日 (Saturday)
1999-12-31金曜日 (Friday)
1985-04-12金曜日 (Friday)
1990-12-31月曜日 (Monday)

RFC 3339が曜日を含まない理由

1. 冗長情報

曜日は日付から正確に計算できるため、含めると潜在的な不整合が発生します:

誤った例:
"Monday, 2002-07-16T10:00:00Z"

問題: 2002-07-16は実際には火曜日で、月曜日ではありません
曜日と日付のどちらを信頼すべきですか?

2. 複雑性の増加

パーサーは曜日と日付間の検証と不整合を処理する必要があります。

3. ローカライゼーションの問題

曜日の名前は言語によって異なります:

英語: Monday, Tuesday, Wednesday, ...
フランス語: Lundi, Mardi, Mercredi, ...
日本語: 月曜日, 火曜日, 水曜日, ...

4. 時点に影響しない

曜日は時点の決定に影響せず、人間の可読性のためだけのものです。

推奨事項

曜日を表示する必要がある場合:

from datetime import datetime

# RFC 3339タイムスタンプを解析
timestamp = "2002-07-15T10:00:00Z"
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))

# 曜日を計算して表示
day_name = dt.strftime('%A')
print(f"{timestamp} is a {day_name}")
# 出力: 2002-07-15T10:00:00Z is a Monday

結論: 曜日は日付から正確かつ決定論的に計算できるため、タイムスタンプ形式に含めることは不必要であるだけでなく、有害です。