项目里有一个按配置时区取日期的工具函数,同时还有零散的 time.strftime("%Y-%m-%d")。开发机的时区和配置一样,两种写法永远同一天;CI 跑在 UTC,北京时间零点到八点之间,读的一方会去找前一天的文件。
time.strftime、time.localtime、date.today()、不带 tz 的 datetime.now(),用的都是进程的系统时区。项目里如果另有一个按配置时区取日期的函数,两种写法在「系统时区等于配置时区」的机器上结果永远一样,换一台机器才会分开。
写学习记录的一方按配置时区(默认 Asia/Shanghai)决定落到哪天的文件:
day = str(timestamp or "")[:10] or now_str("%Y-%m-%d") # 配置时区生成每日摘要的一方,日期参数为空时这样兜底:
date_key = str(date or "").strip() or time.strftime("%Y-%m-%d") # 系统时区开发机是 Windows、GMT+8,两行永远得到同一天,测试全绿。GitHub Actions 的 runner 是 UTC,北京时间 0 点到 8 点之间,写入方已经是今天,摘要方还是昨天,去读昨天的事件文件,读到的是空的。CI 上的表现是摘要里「当天讲过的知识点」为空,看起来像数据没写进去,不像日期问题。修好之前 CI 连挂了 5 次。
修法是两处兜底都改成配置时区的 today_str()。当时想在本地复现,发现 Windows 上没有 time.tzset(),设 TZ=UTC 不起作用,只能 monkeypatch 固定日期函数。
后来想到一个更直接的办法:不改系统时区,改配置时区。把配置时区设成离开发机很远的地方,两种口径就会在一天的大部分时间里分开,任何操作系统上都能复现。我在 UTC+8 下逐小时算过,配成 UTC 每天有 8 小时不在同一天,配成 Pacific/Honolulu(UTC-10)有 18 小时:
@pytest.fixture
def far_away_tz(monkeypatch):
monkeypatch.setattr(settings.system, "timezone", "Pacific/Honolulu")在主项目里我还没把这个 fixture 加进去,只记进了修复建议。HEAD 上 time.strftime 取日期的写法已经清掉了,但 date.today() 还有 10 处,ruff 的 DTZ011 能把它们一次找出来。