日期时间算法是处理日期和时间计算、转换、格式化、解析的算法集合。在日常生活中,日期时间计算无处不在,从简单的日历显示到复杂的时区转换、闰年判断、日期间隔计算等,都需要精确的算法支持。
时间表示的演进示意:
时间表示方式的发展:
古代 → 日晷、沙漏 (物理测量)
↓
机械时代 → 机械钟表 (齿轮传动)
↓
电子时代 → 石英振荡 (32768 Hz)
↓
数字时代 → 时间戳 (Unix Timestamp)
从1970-01-01 00:00:00 UTC开始计算的秒数/毫秒数
计算机中的时间存储:
┌─────────────────────────────────────────┐
│ Unix时间戳: 1704067200 │
│ 表示: 2024-01-01 00:00:00 UTC │
│ 存储: 32位/64位整数 │
└─────────────────────────────────────────┘
- 格里高利历(公历):国际通用的阳历系统
- 闰年规则:4年一闰,100年不闰,400年再闰
- 时区:全球24个时区,夏令时(DST)调整
- 时间戳:Unix纪元以来的秒数/毫秒数
完整日期时间结构示意:
2024-03-15 14:30:45.123 UTC+8
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ └── 时区偏移
│ │ │ │ │ │ └─────── 毫秒
│ │ │ │ │ └────────── 秒
│ │ │ │ └───────────── 分
│ │ │ └──────────────── 时
│ │ └─────────────────── 日
│ └────────────────────── 月
└─────────────────────────── 年
各部分的取值范围:
┌─────────┬───────────┬────────────────────────┐
│ 字段 │ 范围 │ 说明 │
├─────────┼───────────┼────────────────────────┤
│ 年 │ 1-9999 │ 格里高利历有效范围 │
│ 月 │ 1-12 │ │
│ 日 │ 1-31 │ 依月份和闰年变化 │
│ 时 │ 0-23 │ 24小时制 │
│ 分 │ 0-59 │ │
│ 秒 │ 0-59 │ 闰秒时可能为60 │
│ 毫秒 │ 0-999 │ │
│ 时区 │ UTC-12 │ 到 UTC+14 │
└─────────┴───────────┴────────────────────────┘
优点:
- 标准化:ISO 8601标准统一了日期时间表示
- 数字化:时间戳便于计算机存储和计算
- 库支持:现代编程语言都有完善的时间库
缺点:
- 闰年复杂性:2月天数不固定
- 时区转换:夏令时带来额外复杂度
- 历史历法:不同地区曾使用不同历法
- 精度问题:浮点数表示时间的精度损失
概述:
- 判断给定年份是否为闰年
- 闰年2月有29天,平年2月有28天
闰年规则示意:
闰年判断流程:
年份 year
│
├── year % 4 != 0 ──→ 平年 (如: 2023)
│
├── year % 100 != 0 ──→ 闰年 (如: 2024)
│
├── year % 400 != 0 ──→ 平年 (如: 2100)
│
└── 闰年 (如: 2000)
判定逻辑:
(year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)
示例验证:
┌──────┬──────────┬──────────┬───────────┬────────┐
│ 年份 │ % 4 == 0 │ %100 !=0 │ %400 == 0 │ 结果 │
├──────┼──────────┼──────────┼───────────┼────────┤
│ 2000 │ ✓ │ ✗ │ ✓ │ 闰年 │
│ 2024 │ ✓ │ ✓ │ - │ 闰年 │
│ 2100 │ ✓ │ ✗ │ ✗ │ 平年 │
│ 2023 │ ✗ │ - │ - │ 平年 │
└──────┴──────────┴──────────┴───────────┴────────┘
核心思想:
- 能被4整除但不能被100整除,或者能被400整除
应用:
- 日历生成
- 日期间隔计算
- 年龄计算
实现文件:
isLeapYear.c
概述:
- 根据日期计算星期几
- 多种算法:Zeller公式、基姆拉尔森公式、蔡勒公式
Zeller公式示意:
星期计算公式 (Zeller's Congruence):
对于格里高利历 (1582年之后):
h = (q + [13×(m+1)/5] + K + [K/4] + [J/4] + 5×J) mod 7
变量说明:
┌───────┬────────────────────────────────────┐
│ 变量 │ 说明 │
├───────┼────────────────────────────────────┤
│ h │ 星期几 (0=周六, 1=周日, ..., 6=周五) │
│ q │ 日期 (1-31) │
│ m │ 月份 (3=3月, ..., 14=2月) │
│ │ 注意: 1月和2月视为上一年的13月和14月 │
│ K │ 年份 % 100 (世纪内的年份) │
│ J │ 年份 / 100 (零基世纪) │
└───────┴────────────────────────────────────┘
示例: 2024年3月15日
┌──────────┬─────────┐
│ 变量 │ 值 │
├──────────┼─────────┤
│ q │ 15 │
│ m │ 3 │
│ K │ 24 │ (2024 % 100)
│ J │ 20 │ (2024 / 100)
└──────────┴─────────┘
计算:
h = (15 + [13×4/5] + 24 + [24/4] + [20/4] + 5×20) mod 7
= (15 + 10 + 24 + 6 + 5 + 100) mod 7
= 160 mod 7
= 6 (星期五)
验证: 2024年3月15日确实是星期五
核心思想:
- 利用数学公式将日期映射到星期
- 考虑了每月天数差异和闰年影响
应用:
- 日历显示
- 日期验证
- 排班系统
实现文件:
calendar.c/calendar_simple.c
概述:
- 生成指定年月的日历视图
- 处理月份天数、星期对齐、闰年逻辑
日历布局示意:
2024年3月日历:
日 一 二 三 四 五 六
━━━ ━━━ ━━━ ━━━ ━━━ ━━━ ━━━
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
算法步骤:
1. 确定该月1日是星期几
2. 确定该月有多少天 (考虑闰年)
3. 从正确的星期位置开始填充日期
4. 补齐首尾空白
2024年各月天数:
┌──────┬──────┬──────┬──────┬──────┬──────┐
│ 月 │ 天数 │ 月 │ 天数 │ 月 │ 天数 │
├──────┼──────┼──────┼──────┼──────┼──────┤
│ 1 │ 31 │ 4 │ 30 │ 7 │ 31 │
│ 2 │ 29 │ 5 │ 31 │ 8 │ 31 │
│ 3 │ 31 │ 6 │ 30 │ 9 │ 30 │
│ │ │ │ │ 10 │ 31 │
│ │ │ │ │ 11 │ 30 │
│ │ │ │ │ 12 │ 31 │
└──────┴──────┴──────┴──────┴──────┴──────┘
核心思想:
- 计算起始星期偏移
- 按7列布局填充日期
应用:
- 日历应用
- 日程管理系统
- 日期选择器
实现文件:
calendar.c
概述:
- 计算两个日期之间的天数、月数、年数
- 考虑闰年和不同月份天数
日期间隔计算示意:
计算 2023-01-15 到 2024-03-20 的间隔:
方法1: 转换为时间戳差
┌────────────────────────────────────────┐
│ 日期1: 2023-01-15 = 1673740800 (Unix) │
│ 日期2: 2024-03-20 = 1710902400 (Unix) │
│ 差值: 1710902400 - 1673740800 │
│ = 37161600 秒 │
│ = 430 天 │
└────────────────────────────────────────┘
方法2: 逐日计算 (考虑闰年)
┌────────────────────────────────────────┐
│ 2023-01-15 到 2024-01-15 = 366天 │
│ (2024是闰年,过了2月29日) │
│ │
│ 2024-01-15 到 2024-03-20: │
│ 1月剩余: 31-15 = 16天 │
│ 2月完整: 29天 (闰年) │
│ 3月已过: 20天 │
│ 小计: 16 + 29 + 20 = 65天 │
│ │
│ 总计: 366 + 65 = 431天 ?? │
│ 纠正: 实际上应该是430天 │
│ 说明: 跨闰年日期的计算需要仔细处理 │
└────────────────────────────────────────┘
核心思想:
- 转换为时间戳计算最简单
- 或直接计算各时间段累加
应用:
- 年龄计算
- 合同期限
- 项目工期
实现文件:
calendar.c中的日期计算函数
概述:
- 摄氏温度(°C)、华氏温度(°F)、开尔文温度(K)之间的转换
温度转换公式示意:
温度转换公式:
┌────────────────────────────────────────────────────────┐
│ 摄氏 → 华氏: °F = °C × 9/5 + 32 │
│ 华氏 → 摄氏: °C = (°F - 32) × 5/9 │
│ 摄氏 → 开尔文: K = °C + 273.15 │
│ 开尔文 → 摄氏: °C = K - 273.15 │
└────────────────────────────────────────────────────────┘
常用温度对照:
┌────────────┬────────────┬────────────┐
│ 摄氏°C │ 华氏°F │ 开尔文K │
├────────────┼────────────┼────────────┤
│ -273.15 │ -459.67 │ 0 │ 绝对零度
│ 0 │ 32 │ 273.15 │ 冰点
│ 37 │ 98.6 │ 310.15 │ 人体体温
│ 100 │ 212 │ 373.15 │ 沸点
└────────────┴────────────┴────────────┘
核心思想:
- 线性转换关系
- 注意浮点数精度
应用:
- 天气预报
- 科学计算
- 日常生活
实现文件:
celsiusToFahrenheit.c
- 日志系统:时间戳记录、日志轮转
- 定时任务:Cron表达式解析、任务调度
- 缓存系统:过期时间计算、TTL管理
- 会话管理:Session过期、Token有效期
- 金融领域:交易日计算、利息计算、到期日
- 人力资源:考勤统计、工时计算、排班
- 电子商务:订单时效、促销倒计时、预约系统
- 医疗健康:用药提醒、随访周期、生理周期
- 天文计算:星历表、日出日落时间
- 数据分析:时间序列分析、趋势预测
- 时区处理:跨国会议时间、航班时刻表
Unix时间戳的优点:
┌─────────────────────────────────────────┐
│ • 单一整数,存储高效 │
│ • 比较大小简单 (数值比较) │
│ • 时区无关,便于国际传输 │
│ • 计算间隔只需减法 │
│ • 排序高效 │
└─────────────────────────────────────────┘
转换注意事项:
┌─────────────────────────────────────────┐
│ • 注意秒级 vs 毫秒级时间戳 │
│ • 处理时区转换要严谨 │
│ • 闰秒是特殊情况 (23:59:60) │
│ • 2038年问题 (32位系统) │
└─────────────────────────────────────────┘
- ISO 8601:
YYYY-MM-DDTHH:mm:ss.sssZ(国际标准) - RFC 2822:
Mon, 15 Mar 2024 14:30:00 GMT - 本地化格式:根据用户地区显示不同格式
- 存储:使用UTC时间存储
- 传输:使用ISO 8601格式带时区信息
- 显示:根据用户时区本地化显示
- 计算:先转UTC计算,再转回本地
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 闰年判断 | O(1) | 简单取模运算 |
| 星期计算 | O(1) | 数学公式直接计算 |
| 日期间隔 | O(1) | 时间戳差或公式计算 |
| 日历生成 | O(n) | n为该月天数 |
| 日期解析 | O(1) | 字符串解析和转换 |
- 日期时间对象通常占用8-16字节
- 时间戳存储仅需4-8字节
- C:标准库
<time.h>,struct tm结构 - Java:
java.time包(Java 8+) - Go:
time包,Location时区处理 - JavaScript:
Date对象,推荐Temporal API(未来) - Python:
datetime模块,时区支持完善 - Rust:
chrono库,类型安全的时间处理
- 基础概念:理解时间戳、时区、闰年规则
- 日期计算:掌握日期间隔、星期计算
- 格式处理:学会解析和格式化日期字符串
- 时区进阶:处理夏令时、多时区转换
- 始终使用标准库处理日期时间,避免手写
- 存储和传输使用UTC,仅在显示时转换
- 注意边界情况:闰年、闰秒、时区边界
- 测试时覆盖各种边界日期(2月29日、12月31日等)
- 2038年问题 awareness(32位时间戳溢出)