px普贤教程PuXian
普贤教程 › 生态包

时间与日历

时间是最容易出「差 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 会返回 Errhd_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)。多于一种就统一成一种。