本文目录导读:

脚本中处理日期时间确实是一个充满“陷阱”的领域,很多看似简单的操作都可能因为时区、格式、闰秒、夏令时等问题导致线上故障,下面总结最常见的几类坑及应对策略。
时区问题(最常踩的坑)
服务器时区不一致
- 坑:开发环境用UTC,生产用CST(北京时间),导致时间差8小时
- 示例:
# 错误的做法 now = datetime.now() # 取决于服务器时区
- 解决:始终使用UTC存储,展示时转换
from datetime import datetime, timezone now = datetime.now(timezone.utc)
数据库时区处理
- 坑:MySQL TIMESTAMP自动转换UTC,DATETIME不转换但读写隐含时区
- 解决:统一使用TIMESTAMP+UTC时区,或者统一用时间戳
-- 推荐:使用时间戳 CREATE TABLE events ( event_time BIGINT NOT NULL, -- Unix时间戳 ... );
日期格式化陷阱
月份和分钟占位符混淆
- 坑:
M代表月份,m代表分钟# 错误 datetime.strptime("2023-04-05 13:45", "%Y-%m-%d %H:%M") # 正确 datetime.strptime("2023-04-05 13:45", "%Y-%M-%d %H:%m") # 错误!
12/24小时制混淆
- 坑:
%I是12小时制(01-12),%H是24小时制(00-23)# 如果中午12点在12小时制中是12:00 PM,24小时制中是12:00 datetime.strptime("12:00 PM", "%I:%M %p") # 正确 datetime.strptime("12:00", "%I:%M") # 可能得到凌晨0点
闰秒和闰年问题
闰秒
- 坑:大多数系统忽略闰秒(23:59:60),但某些金融系统需要精确
- 解决:使用支持闰秒的库(如
astropy.time),或接受±1秒误差
闰年判断
-
坑:2月29日不是每年都有,闰年规则:能被4整除但不能被100整除,或能被400整除
-
示例:
# 错误:直接硬编码 if month == 2 and day > 28: # 错误!有的年份2月有29天 # 正确 import calendar calendar.isleap(2024) # True
夏令时(DST)问题
时间不存在或重复
-
坑:
- 春季:2:00 AM 直接跳到 3:00 AM,2:30 AM不存在
- 秋季:1:00 AM 出现两次(第一次是夏令时,第二次是标准时间)
-
解决:使用
pytz或zoneinfo时指定is_dst=None(强制报错)from datetime import datetime import pytz chicago = pytz.timezone('America/Chicago') # 错误:ambiguous time chicago.localize(datetime(2023, 11, 5, 1, 30)) # 可能报错 # 正确:指定is_dst chicago.localize(datetime(2023, 11, 5, 1, 30), is_dst=True) # 夏令时版本 chicago.localize(datetime(2023, 11, 5, 1, 30), is_dst=False) # 标准时间版本
计算误区
日期加减跨越月份/年份
-
坑:
+30天不等于+1个月from datetime import datetime, timedelta # 错误:1月31日 + 1个月 = 2月28/29日,但超过天数可能被截断 date = datetime(2023, 1, 31) + timedelta(days=30) # 得3月2日,不是2月28日 # 正确:使用dateutil.relativedelta from dateutil.relativedelta import relativedelta date + relativedelta(months=1) # 得2月28日(2023年非闰年)
时间比较忽略时区
-
坑:没有时区的
datetime可能与有时区的直接比较# 错误 naive_dt = datetime(2024, 1, 1, 0, 0) # 没有时区 aware_dt = datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc) naive_dt > aware_dt # TypeError # 正确:统一有时区 datetime(2024, 1, 1, 0, 0, tzinfo=timezone.utc) > aware_dt
跨语言/系统兼容问题
时间精度差异
- 坑:Python的
datetime支持微秒,JavaScript的Date只支持毫秒 - 解决:存储时统一到毫秒精度,或使用字符串格式
时间戳单位混淆
-
坑:秒级 vs 毫秒级 vs 微秒级
# 错误:假设都是秒级 timestamp_ms = 1700000000000 # 毫秒 datetime.fromtimestamp(timestamp_ms) # 错误:会得到非常大的年份 # 正确 timestamp_s = timestamp_ms / 1000 datetime.fromtimestamp(timestamp_s)
库选择陷阱
不同库的行为差异
-
坑:
datetime、pytz、dateutil、arrow处理方式不同 -
示例:
# pytz:需要localize(),不能直接用tzinfo import pytz tz = pytz.timezone('Asia/Shanghai') dt = tz.localize(datetime(2024, 1, 1, 12, 0)) # 正确 # 错误:datetime(2024, 1, 1, 12, 0, tzinfo=tz) # pytz不推荐这样用
最佳实践总结
- 永远使用UTC存储,只在展示时转换为本地时间
- 数据库中使用timestamp类型,或统一存储Unix时间戳
- 使用带时区的datetime对象(
aware对象) - 日期计算使用
dateutil.relativedelta,不用timedelta按天算 - 避免直接字符串比较,用datetime对象比较
- 测试边界时间:2月29日、夏令时切换、午夜零点、跨越世纪的年份
- 使用成熟的库:
- Python:
arrow(推荐)、pendulum、delorean - JavaScript:
moment.js(已弃用,推荐luxon或date-fns) - 通用:
Day.js(轻量级)
- Python:
快速检查清单
- [ ] 所有时间存储是否使用UTC?
- [ ] 数据库中的时间字段是否包含时区信息?
- [ ] 时间计算是否考虑了闰年和月份天数?
- [ ] 夏令时切换的时间段是否被正确处理?
- [ ] 不同系统间的时间精度是否一致?
- [ ] 前端和后端的时间格式是否匹配?
- [ ] 日志中的时间是否都是标准格式(如ISO 8601)?
处理日期时间时,永远假设会有意想不到的情况发生,尤其是在跨时区、跨平台、跨系统的场景下。