Appendix C. Leap Years (附录 C. 闰年)
本附录详细说明了闰年计算的规则和实现。
Leap Year Rules (闰年规则)
公历中的闰年规则旨在使平均年长度接近地球绕太阳运行的实际周期 (约 365.2422 天)。
Three Rules (三条规则)
闰年必须满足以下条件之一:
- 能被 4 整除但不能被 100 整除的年份是闰年
- 能被 400 整除的年份是闰年
伪代码:
if (year % 400 == 0) then
return LEAP_YEAR
else if (year % 100 == 0) then
return NOT_LEAP_YEAR
else if (year % 4 == 0) then
return LEAP_YEAR
else
return NOT_LEAP_YEAR
Implementation Examples
C Language
int is_leap_year(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// Or a clearer version
int is_leap_year_verbose(int year) {
if (year % 400 == 0) {
return 1; // Divisible by 400: leap year
}
if (year % 100 == 0) {
return 0; // Divisible by 100 but not 400: common year
}
if (year % 4 == 0) {
return 1; // Divisible by 4 but not 100: leap year
}
return 0; // Otherwise: common year
}
Python
def is_leap_year(year):
"""Determine if year is a leap year"""
return (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0)
# Or use Python calendar module
import calendar
print(calendar.isleap(2000)) # True
JavaScript
function isLeapYear(year) {
return (year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0);
}
Java
public static boolean isLeapYear(int year) {
return (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0);
}
// Or use Java 8+ GregorianCalendar
import java.util.GregorianCalendar;
boolean isLeap = GregorianCalendar.isLeapYear(2000);
Historical Leap Year Examples
Recent Leap Years (21st Century)
2000: Leap year ✅ (divisible by 400)
2001: Common year ❌
2002: Common year ❌
2003: Common year ❌
2004: Leap year ✅ (divisible by 4, not by 100)
2008: Leap year ✅
2012: Leap year ✅
2016: Leap year ✅
2020: Leap year ✅
2024: Leap year ✅
2028: Leap year ✅
Century Years (Special Cases)
1600: Leap year ✅ (divisible by 400)
1700: Common year ❌ (divisible by 100 but not 400)
1800: Common year ❌ (divisible by 100 but not 400)
1900: Common year ❌ (divisible by 100 but not 400)
2000: Leap year ✅ (divisible by 400)
2100: Common year ❌ (divisible by 100 but not 400)
2200: Common year ❌
2300: Common year ❌
2400: Leap year ✅ (divisible by 400)
Days in Each Month
Understanding leap years is crucial for determining days in each month:
def days_in_month(year, month):
"""Return number of days in specified year and month"""
days_per_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if month == 2 and is_leap_year(year):
return 29
return days_per_month[month - 1]
# Examples
print(days_in_month(2000, 2)) # 29 (leap year February)
print(days_in_month(1900, 2)) # 28 (common year February)
print(days_in_month(2024, 4)) # 30 (April always 30 days)
Date Validation
def is_valid_date(year, month, day):
"""Validate if date is valid"""
if month < 1 or month > 12:
return False
if day < 1:
return False
max_day = days_in_month(year, month)
if day > max_day:
return False
return True
# Tests
print(is_valid_date(2000, 2, 29)) # True (leap year)
print(is_valid_date(1900, 2, 29)) # False (common year)
print(is_valid_date(2002, 4, 31)) # False (April has only 30 days)
History of Leap Years
Julian vs Gregorian Calendar
Julian Calendar:
- Introduced by Julius Caesar in 46 BC
- Simple rule: leap year every 4 years
- Average year length: 365.25 days
- Problem: about 11 minutes longer than actual solar year
Gregorian Calendar:
- Introduced by Pope Gregory XIII in 1582
- Modified rule: century years must be divisible by 400
- Average year length: 365.2425 days
- Closer to actual solar year (365.2422 days)
Adoption of Gregorian Calendar
Different countries adopted the Gregorian calendar at different times:
1582: Italy, Spain, Portugal
1752: Great Britain and colonies (including US)
1917: Russia
1923: Greece
Why Do We Need Leap Years? (为什么需要闰年?)
地球绕太阳运行的实际周期约为 365.2422 天, 而不是正好 365 天。
没有闰年的后果:
年度误差: 0.2422 天 ≈ 5 小时 48 分 46 秒
4 年后: 约 1 天
100 年后: 约 24 天
400 年后: 约 97 天
季节会逐渐漂移!
Gregorian Calendar Accuracy (公历准确性):
400 年中的闰年: 97
(400 年中能被 4 整除的年份: 100
减去能被 100 整除的: 4
加上能被 400 整除的: 1
= 100 - 4 + 1 = 97)
400 年中的总天数: 365 × 400 + 97 = 146,097 天
年平均: 146,097 ÷ 400 = 365.2425 天
误差: 365.2425 - 365.2422 = 0.0003 天/年
大约需要 3,333 年才能累积 1 天误差
RFC 3339 Implementation Requirements (RFC 3339 实现要求)
实现 RFC 3339 时, 必须正确处理闰年:
# 验证 RFC 3339 日期-时间
def validate_rfc3339_date(year, month, day):
"""验证 RFC 3339 格式的日期部分"""
# 年份范围检查
if year < 0 or year > 9999:
return False, "Year must be 0000-9999"
# 月份检查
if month < 1 or month > 12:
return False, "Month must be 01-12"
# 日期检查 (考虑闰年)
if not is_valid_date(year, month, day):
max_day = days_in_month(year, month)
return False, f"Day must be 01-{max_day:02d} for {year}-{month:02d}"
return True, "Valid date"
# 测试
print(validate_rfc3339_date(2000, 2, 29)) # (True, 'Valid date')
print(validate_rfc3339_date(1900, 2, 29)) # (False, '...')
Key Point (关键点): 正确实现闰年规则对于 RFC 3339 合规性至关重要。不正确的闰年确定可能导致接受无效日期或拒绝有效日期。