Skip to main content

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

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

Zeller's Congruence (蔡勒公式)

计算星期几的常用算法是Zeller's Congruence(蔡勒一致性),由Christian Zeller于1882年发明。

公式

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实现

def day_of_week_zeller(year, month, day):
"""
使用Zeller公式计算星期几
返回: 0=星期六, 1=星期日, ..., 6=星期五
"""
# 一月和二月作为上一年的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=Sat, 1=Sun, 2=Mon, 3=Tue, 4=Wed, 5=Thu, 6=Fri
# 调整为: 0=Mon, 1=Tue, 2=Wed, 3=Thu, 4=Fri, 5=Sat, 6=Sun
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星期一
2000-01-01星期六
1999-12-31星期五
1985-04-12星期五
1990-12-31星期一

为什么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

结论: 由于星期几可以从日期精确且确定性地计算,因此将其包含在时间戳格式中不仅是不必要的,而且有害。