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