普贤教程 › 生态包
时间与日历
时间是最容易出「差 8 小时」的地方。这一组包:datetime 做运算与格式化、holidays 判工作日、lunar 农历、tzmini 时区。本页示例为本机实测。
先记住一件事:UTC 还是本地
px
import datetime var sec = 1762340000 print(dt_format(sec, "%Y-%m-%d %H:%M:%S")) # 2025-11-05 10:53:20 ← UTC print(dt_iso_format(sec)) # 2025-11-05T10:53:20Z
dt_format 输出 UTC,不是北京时间同一时刻在东八区是 18:53:20。写日志、给用户看时间时,先加 8 小时或用 tzmini 换算。生产上踩过的坑:队列的时间列一边写 UTC、一边写本地,结果「任务永远在未来」,队列静默不工作。统一口径:存储与比较一律 UTC 秒,只在展示层转本地。常用运算
px
print(dt_add_days(1762340000, 30)) # 1764932000 print(dt_days_between(1762340000, 1764932000))# 30 print(dt_weekday(2026, 11, 5)) # 3 ← 0=周一 … 6=周日 print(dt_quarter(2026, 11)) # 4 print(dt_week_of_year(2026, 11, 5)) # 45 ← ISO 8601 周数 print(dt_humane(1762340000, 1762340060)) # 1分钟后
dt_weekday 的 0 是周一不是周日(与 strftime("%w") 的 0=周日不同)。做「每周一几点」这类调度时,写错一天会很难发现。工作日与节假日
px
import holidays print(hd_is_workday(2025, 10, 1)) # Ok(false) ← 国庆 print(hd_is_workday(2025, 9, 28)) # Ok(true) ← 调休上班 print(hd_next_workday(2025, 10, 8)) # Ok([2025, 10, 9]) print(hd_holidays(2025).ok()[0:2]) # [{d: 1, m: 1, name: 元旦}, {d: 28, m: 1, name: 春节}]
数据表只到 2025,2026 会返回 Err
hd_is_workday(2026, 1, 1) → Err(holidays: 年份未收录(当前数据表 2024-2025): 2026)。
这不是 bug,是「国务院没公布就没有数据」的诚实做法。跨年任务要判 is_err() 并回落到「周一至周五算工作日」。农历
px
import lunar print(lr_solar_to_lunar(2026, 11, 5)) # 公历 → 农历 print(lr_lunar_to_solar(2026, 9, 27)) # 农历 → 公历
支持 1900–2100。判断春节、中秋这类节日用它;判断「法定假日」用 holidays(因为含调休)。
时区
px
import tzmini print(tz_offset("Asia/Shanghai")) # 8.0 print(tz_convert(1762340000, "UTC", "Asia/Shanghai")) print(tz_zone_names()) # 支持的时区名
时间处理的四条纪律
| 纪律 | 原因 |
|---|---|
| 存 UTC 秒(整数) | 可比、可排序、无时区歧义 |
| 展示层才转本地 | 用户看北京时间,但存储不该带时区 |
| 比较用整数,不用字符串 | "2026-11-05" < "2026-11-4" 字符串比较是错的 |
| 跨时区业务(如国际客户)存「时刻 + 时区 ID」 | 只存时刻,无法还原「当地几点」 |
一个自查任何「时间差 8 小时 / 永远取不到 / 刚插入显示在未来」的 bug,第一步先查:这条链路上有几种时间来源(DB 的
NOW()、脚本 now_sec()、dt_format 的 UTC)。多于一种就统一成一种。