Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

日期时间(Date Time)算法概述

1. 什么是日期时间算法?

日期时间算法是处理日期和时间计算、转换、格式化、解析的算法集合。在日常生活中,日期时间计算无处不在,从简单的日历显示到复杂的时区转换、闰年判断、日期间隔计算等,都需要精确的算法支持。

时间表示的演进示意

时间表示方式的发展:

古代 → 日晷、沙漏 (物理测量)
         ↓
机械时代 → 机械钟表 (齿轮传动)
         ↓
电子时代 → 石英振荡 (32768 Hz)
         ↓
数字时代 → 时间戳 (Unix Timestamp)
           从1970-01-01 00:00:00 UTC开始计算的秒数/毫秒数

计算机中的时间存储:
┌─────────────────────────────────────────┐
│  Unix时间戳: 1704067200                  │
│  表示: 2024-01-01 00:00:00 UTC           │
│  存储: 32位/64位整数                      │
└─────────────────────────────────────────┘

2. 日期时间的基本特性

2.1 时间系统的复杂性

  • 格里高利历(公历):国际通用的阳历系统
  • 闰年规则:4年一闰,100年不闰,400年再闰
  • 时区:全球24个时区,夏令时(DST)调整
  • 时间戳:Unix纪元以来的秒数/毫秒数

2.2 日期时间的组成

完整日期时间结构示意:

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              │
└─────────┴───────────┴────────────────────────┘

2.3 日期时间计算的难点

优点

  • 标准化:ISO 8601标准统一了日期时间表示
  • 数字化:时间戳便于计算机存储和计算
  • 库支持:现代编程语言都有完善的时间库

缺点

  • 闰年复杂性:2月天数不固定
  • 时区转换:夏令时带来额外复杂度
  • 历史历法:不同地区曾使用不同历法
  • 精度问题:浮点数表示时间的精度损失

3. 常见的日期时间算法

3.1 闰年判断(Leap Year)

概述

  • 判断给定年份是否为闰年
  • 闰年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

3.2 星期计算(Zeller's Congruence)

概述

  • 根据日期计算星期几
  • 多种算法: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

3.3 日历生成(Calendar Generation)

概述

  • 生成指定年月的日历视图
  • 处理月份天数、星期对齐、闰年逻辑

日历布局示意

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

3.4 日期间隔计算(Date Diff)

概述

  • 计算两个日期之间的天数、月数、年数
  • 考虑闰年和不同月份天数

日期间隔计算示意

计算 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 中的日期计算函数

3.5 温度转换(Temperature Conversion)

概述

  • 摄氏温度(°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

4. 典型应用场景

4.1 系统开发

  • 日志系统:时间戳记录、日志轮转
  • 定时任务:Cron表达式解析、任务调度
  • 缓存系统:过期时间计算、TTL管理
  • 会话管理:Session过期、Token有效期

4.2 业务应用

  • 金融领域:交易日计算、利息计算、到期日
  • 人力资源:考勤统计、工时计算、排班
  • 电子商务:订单时效、促销倒计时、预约系统
  • 医疗健康:用药提醒、随访周期、生理周期

4.3 科学计算

  • 天文计算:星历表、日出日落时间
  • 数据分析:时间序列分析、趋势预测
  • 时区处理:跨国会议时间、航班时刻表

5. 算法技巧总结

5.1 时间戳转换技巧

Unix时间戳的优点:
┌─────────────────────────────────────────┐
│ • 单一整数,存储高效                       │
│ • 比较大小简单 (数值比较)                  │
│ • 时区无关,便于国际传输                   │
│ • 计算间隔只需减法                        │
│ • 排序高效                               │
└─────────────────────────────────────────┘

转换注意事项:
┌─────────────────────────────────────────┐
│ • 注意秒级 vs 毫秒级时间戳                 │
│ • 处理时区转换要严谨                       │
│ • 闰秒是特殊情况 (23:59:60)               │
│ • 2038年问题 (32位系统)                  │
└─────────────────────────────────────────┘

5.2 日期格式化技巧

  • ISO 8601YYYY-MM-DDTHH:mm:ss.sssZ(国际标准)
  • RFC 2822Mon, 15 Mar 2024 14:30:00 GMT
  • 本地化格式:根据用户地区显示不同格式

5.3 时区处理原则

  • 存储:使用UTC时间存储
  • 传输:使用ISO 8601格式带时区信息
  • 显示:根据用户时区本地化显示
  • 计算:先转UTC计算,再转回本地

6. 性能特点

6.1 时间复杂度

操作 时间复杂度 说明
闰年判断 O(1) 简单取模运算
星期计算 O(1) 数学公式直接计算
日期间隔 O(1) 时间戳差或公式计算
日历生成 O(n) n为该月天数
日期解析 O(1) 字符串解析和转换

6.2 空间复杂度

  • 日期时间对象通常占用8-16字节
  • 时间戳存储仅需4-8字节

7. 编程语言支持

  • C:标准库<time.h>,struct tm结构
  • Javajava.time包(Java 8+)
  • Gotime包,Location时区处理
  • JavaScriptDate对象,推荐 Temporal API(未来)
  • Pythondatetime模块,时区支持完善
  • Rustchrono库,类型安全的时间处理

8. 学习建议

8.1 学习路径

  1. 基础概念:理解时间戳、时区、闰年规则
  2. 日期计算:掌握日期间隔、星期计算
  3. 格式处理:学会解析和格式化日期字符串
  4. 时区进阶:处理夏令时、多时区转换

8.2 实践要点

  • 始终使用标准库处理日期时间,避免手写
  • 存储和传输使用UTC,仅在显示时转换
  • 注意边界情况:闰年、闰秒、时区边界
  • 测试时覆盖各种边界日期(2月29日、12月31日等)
  • 2038年问题 awareness(32位时间戳溢出)