Skip to main content

Appendix B. Day of the Week (附录 B. 星期几)

本附录展示了如何从任何公历日期计算星期几。这对于理解为什么 RFC 3339 不包含星期几信息很重要——因为它可以精确计算。

Zeller's Congruence (泽勒同余)

计算星期几的常用算法是泽勒同余 (Zeller's Congruence), 由 Christian Zeller 于 1882 年发明。

Formula (公式)

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 = 三月, 4 = 四月, ..., 12 = 十二月, 13 = 一月, 14 = 二月)
  • K: 世纪中的年份 (year % 100)
  • J: 世纪 (⌊year/100⌋)
  • ⌊x⌋: 向下取整函数

注意: 一月和二月被视为上一年的第 13 和第 14 个月。

Python Implementation

def day_of_week_zeller(year, month, day):
"""
Calculate day of week using Zeller's formula
Returns: 0=Saturday, 1=Sunday, ..., 6=Friday
"""
# January and February are treated as months 13 and 14 of previous year
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

# Convert to common format: 0=Monday, ..., 6=Sunday
# Zeller: 0=Sat, 1=Sun, 2=Mon, 3=Tue, 4=Wed, 5=Thu, 6=Fri
# Adjust to: 0=Mon, 1=Tue, 2=Wed, 3=Thu, 4=Fri, 5=Sat, 6=Sun
return (h + 5) % 7

def day_name(year, month, day):
"""Return name of day of week"""
days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday',
'Friday', 'Saturday', 'Sunday']
return days[day_of_week_zeller(year, month, day)]

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

Simpler Algorithm

For programming implementations, a more intuitive algorithm can be used:

def day_of_week_simple(year, month, day):
"""
Simplified day of week calculation
Returns: 0=Monday, ..., 6=Sunday
"""
# Cumulative days before each month (non-leap year)
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 Implementation

function dayOfWeek(year, month, day) {
// JavaScript Date object automatically calculates day of week
const date = new Date(year, month - 1, day);
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday'];
return days[date.getDay()];
}

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

Verification Examples

DateDay of WeekVerified
2002-07-15Monday
2000-01-01Saturday
1999-12-31Friday
1985-04-12Friday
1990-12-31Monday

Why RFC 3339 Doesn't Include Day of Week (为什么 RFC 3339 不包含星期几)

1. Redundant Information (冗余信息)

星期几可以从日期精确计算, 因此包含它会引入潜在的不一致性:

错误示例:
"Monday, 2002-07-16T10:00:00Z"

问题: 2002-07-16 实际上是星期二, 不是星期一
应该信任哪一个? 星期几还是日期?

2. Increased Complexity (增加的复杂性)

解析器需要处理验证和星期几与日期之间的不一致性。

3. Localization Issues (本地化问题)

星期几的名称在不同语言中不同:

英语: Monday, Tuesday, Wednesday, ...
法语: Lundi, Mardi, Mercredi, ...
中文: 星期一, 星期二, 星期三, ...

4. Doesn't Affect Time Point (不影响时间点)

星期几不影响时间点的确定, 它仅用于人类可读性。

Recommendation (建议)

如果需要显示星期几:

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

Conclusion (结论): 由于星期几可以从日期精确且确定地计算, 在时间戳格式中包含它不仅不必要, 而且有害。