time.strftime uses the system time zone: write with the configured zone, read with the system one, green locally and a day off on UTC CI
The project had a helper that takes today’s date in the configured time zone, and scattered time.strftime("%Y-%m-%d") calls next to it. On a dev machine whose zone matches the config, both always agree; CI runs in UTC, and between midnight and 8 a.m. Beijing time the reading side goes looking for yesterday’s file.
time.strftime, time.localtime, date.today() and a tz-less datetime.now() all use the process’s system time zone. If the project also has a helper that takes the date in a configured zone, the two always agree on a machine whose system zone equals the configured one, and only diverge on a different machine.
The side writing learning events chose which day’s file to use by the configured zone (default Asia/Shanghai):
day = str(timestamp or "")[:10] or now_str("%Y-%m-%d") # 配置时区The side building the daily digest fell back like this when no date was passed:
date_key = str(date or "").strip() or time.strftime("%Y-%m-%d") # 系统时区The dev machine was Windows on GMT+8, so both lines always gave the same day and tests passed. The GitHub Actions runner is UTC: between midnight and 8 a.m. Beijing time the writer is already on today while the digest is still on yesterday, reads yesterday’s event file, and finds nothing. On CI it showed up as an empty “concepts taught today” list, which looks like missing data rather than a date problem. CI failed five times in a row before it was fixed.
The fix changed both fallbacks to the configured-zone today_str(). Trying to reproduce locally, I found Windows has no time.tzset(), so setting TZ=UTC does nothing; the only option was to monkeypatch the date function.
Later a more direct option occurred to me: don’t change the system zone, change the configured one. Set the configured zone far from the dev machine and the two will disagree for most of the day, on any OS. Counting hour by hour from UTC+8, configuring UTC gives 8 mismatched hours a day, while Pacific/Honolulu (UTC-10) gives 18:
@pytest.fixture
def far_away_tz(monkeypatch):
monkeypatch.setattr(settings.system, "timezone", "Pacific/Honolulu")I haven’t added this fixture to the main project yet; it’s in my list of fixes. On HEAD the time.strftime date calls are gone, but date.today() still appears ten times, and ruff’s DTZ011 finds all of them at once.