iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
Vibe Coding

一個 Vibe Coding 專案從原型到有人在用系列 第 21 篇

CI 連續綠了 42 輪,換到台灣時間的 Windows 就紅

  • 分享至 

  • xImage
  •  

你的測試在自己電腦上過,CI(每次推上去自動跑的檢查)也過。你會覺得,它換到哪一台電腦都會過嗎?

如果你的程式會處理日期和時間,答案可能是不會。

9 月 18 日晚上,Windows 上 7 個測試失敗

usage 是我做的開源小工具,顯示 Claude Code、Codex 這些 AI 工具用了多少 token、花了多少錢,也會產生一份用量報表。Mac 和 Windows 都能用。

9 月 18 日晚上,我那台 Windows 電腦上的 Claude Code 開了一個 PR(合併請求,#144)。PR 寫,在那台電腦上跑全部的測試,7 個失敗。說明的第一段:

報表相關的 6 支測試只在時區是 UTC 的機器上會過。CI runner 剛好是 UTC 所以一直綠燈,但在 UTC+8 的 Windows 上固定失敗。

UTC 是世界標準時間,台灣是 UTC+8,比它快 8 小時。CI runner 是 GitHub 上替你跑 CI 的那台電腦。(PR 說第 7 個失敗是我那台 Windows 的暫存資料夾設定造成的,跟時區無關,今天不談。)

這 6 個測試,有 4 個是 9 月 13 日加的,那天報表剛改版。usage 每推一次程式到主線(main),CI 就在 Mac 和 Windows 上各跑一輪。從改版到 9 月 18 日開 PR 之前,一共跑了 42 輪,每一輪的 Windows 都是綠的。

測試先把電腦的時區切成 UTC

報表要把每一筆用量算到「哪一天」。同一個時間點,在 UTC 是 5 月 21 日晚上 8 點,在台灣已經是 5 月 22 日凌晨 4 點,日期差了一天。測試的標準答案是用 UTC 寫的,所以 usage 的測試在跑之前,先把程式用的時區切成 UTC,答案才對得上。

usage 的測試是這樣切的:

os.environ["TZ"] = "UTC"
if hasattr(time, "tzset"):
    time.tzset()

第一行把 TZ 這個環境變數(程式讀來決定時區的設定)設成 UTC。後兩行是:如果有 tzset,就叫它讓新設定生效。tzset 是 Python 請作業系統重新讀一次時區設定的功能,Python 文件寫它只在 Unix 這類系統上有,Mac 和 Linux 都算,Windows 沒有。

所以在 Windows 上,這三行改不到程式用的時區,程式照樣用電腦原本的時區。

7 月 15 日,早就知道 Windows 切不動

這件事不是 9 月才知道的。Day 10 寫過 7 月 15 日,usage 的 Windows 版第一次發布那天。那天晚上 7 點合併的 PR #63,是一位第一次來的開發者送的,把 Windows 上跑不起來的測試修好。

PR #63 在測試裡留了註解,寫明改 TZ 這招在 Windows 上沒用(原文是 Windows has no tzset)。所以它換了另一種做法:不切時區,直接把報表裡的換算換成固定用 UTC 的版本。換了兩處:用量算在哪一天、今天是哪一天。

報表裡還有一處,把每段對話的開始時間換成本地時間,這一處沒換。檢查開始時間的測試,到 9 月 13 日才有。

9 月 13 日,報表又多了一處沒換成 UTC

9 月 13 日報表改版,又多一處用到開始時間:報表先做一張「每天一格」的表,再把每段對話放進它開始的那一天。放之前,開始時間一樣先換成本地時間。

在台灣時間的 Windows 上,兩邊對不起來:表上的日子是照 UTC 排的,對話的開始時間卻照台灣時間算,差 8 小時。台灣時間早上 8 點以前開始的對話,日期會比 UTC 多一天;在這幾個測試裡,有的對話被算到表上沒有的那一天,就查不到:

E           KeyError: '2026-05-22'

另一個測試檢查開始時間怎麼顯示。標準答案是 UTC 的 00:00,台灣時間的電腦印出 08:00:

E       AssertionError: assert '2026-09-01 08:00' == '2026-09-01 00:00'

PR 說 CI 那台 Windows 本來就是 UTC。時區本來就是 UTC,測試切不切得動都一樣,所以那 42 輪都是綠的。

https://ithelp.ithome.com.tw/upload/images/20261005/20183178Z8GSYNW5hC.png

在 Mac 上用台灣時間跑,修前修後都過

PR 開好後,我回到 Mac,請 Claude 處理這個 PR。Claude 合併前在 Mac 上跑了報表那兩個測試檔兩次,一次不設時區、一次設成台灣時間,兩次都是 27 passed。

我後來把還沒修的版本拿出來,在同一台 Mac 上用台灣時間跑:

$ TZ=Asia/Taipei pytest tests/test_reporter_build_data.py tests/test_usage_snapshot_replay.py
27 passed in 0.09s

一樣全過。在 Mac 上,測試一開頭就成功把時區切成 UTC,那 8 小時的落差不會出現。

Day 9 遇過一個測試:把修正拿掉,它照樣是綠的,所以它分不出那個修正有沒有作用。這裡是同一件事,只是換成電腦:在 Mac 上跑,修前修後都是 27 個過,這台電腦分不出有沒有修好。當時能分出來的,只有 PR 裡那次 Windows 上的執行。

Mac 切得動,Windows 切不動

Day 17 也遇過「Mac 過、Windows 紅」。那次是型別檢查(mypy,檢查變數型別有沒有用錯的工具)照跑它的那台電腦判斷作業系統,加上 --platform win32 這個開關,在 Mac 上就能照 Windows 的標準檢查一次。

這次沒有像 --platform 那樣一個開關。卡住的不是程式判斷自己在哪個作業系統,而是測試能不能在跑到一半時切換時區:Mac 切得動,Windows 切不動。要撞到它,兩件事要同時成立:切不動時區,而且時區不是 UTC。CI 的 Windows 只有前者,我的 Mac 只有後者。

所以要在 Mac 上撞到它,得自己把「切換時區」關掉。

在 Mac 上模擬「切不動時區」

如果你的測試會在跑到一半時改時區,可以在 Mac 或 Linux 上存一個 like_windows.py(放哪裡都行,它不會改到你的專案):

import os, sys, time
# 模擬 Windows:沒有 tzset,程式執行中改 TZ 也改不到時區
del time.tzset
_set = os._Environ.__setitem__
os._Environ.__setitem__ = lambda self, k, v: None if k == "TZ" else _set(self, k, v)
import pytest
sys.exit(pytest.main(["-q", *sys.argv[1:]]))

del time.tzset 把 tzset 拿掉。只拿掉它還不夠:我試過,Mac 上就算不呼叫 tzset,改 TZ 也會生效;所以下面兩行改掉 Python 寫環境變數的方法:誰要改 TZ,就當作沒這回事。然後用台灣時間跑你的測試:

TZ=Asia/Taipei python like_windows.py tests/

拿 usage 還沒修的版本,跑 tests/test_reporter_build_data.py 和 tests/test_usage_snapshot_replay.py 這兩個檔,失敗的正好是 PR 說明裡列的那 6 個:

6 failed, 21 passed in 0.21s

修好的版本,27 passed。同一個沒修的版本,把時區換成 UTC 再模擬一次,也是 27 passed,跟 CI 一樣綠。

https://ithelp.ithome.com.tw/upload/images/20261005/20183178igc9peFDqh.png

今天可以帶走的

工作單(交給 AI 的任務說明)裡有一格「做完怎麼算對」,Day 4、Day 8 到 Day 20 各在這格加了幾行。今天再加一行(沒用過這張工作單,也可以把這一行直接交給 AI,當作這次改動的驗收要求):

做完怎麼算對:
- (Day 4、Day 8 到 Day 20 加的幾行)
- 改到日期、時間的程式:Day 9 那條「拿掉修正要紅、放回要綠」,要在切不動時區、而且不是 UTC 的環境跑(Windows 本機,或 like_windows.py 加 TZ=Asia/Taipei);只在 CI 或自己的 Mac 上過,不算數。AI 回報要寫明在哪個作業系統、哪個時區跑的

Day 15 到今天這七篇,多半是同一種情況:檢查是綠的,可是它實際看的東西,比你以為的少。翻譯測試只數條目(Day 16),型別檢查只照跑它的那台電腦判斷(Day 17),README 檢查只數小標(Day 20),今天的測試只在 UTC、或切得動時區的電腦上跑過。

Day 1 的路線圖分成五段:下指令、驗收、品質、上線、收尾。前三段到今天結束。明天開始上線這一段:把東西交給陌生人。5 月 22 日,usage 專案開始的第 6 天,第一次有不認識的人在 GitHub 開 issue(問題回報),標題是「0.6.0 以上任一版本都開不了」。

參考資料

  • usage 專案:https://github.com/aqua5230/usage
  • PR #144〈test(report): 釘住工作階段開始時間的時區,非 UTC 機器不再失敗〉:https://github.com/aqua5230/usage/pull/144
  • PR #63〈Fix Windows support: tray startup, test suite, and mypy〉:https://github.com/aqua5230/usage/pull/63
  • 9 月 13 日報表改版:https://github.com/aqua5230/usage/commit/d42496b
  • Python 文件,time.tzset()(只在 Unix 上可用):https://docs.python.org/3/library/time.html#time.tzset

上一篇
README 同步檢查全綠,日文版漏寫了一整個新功能
下一篇
17 小時發了 8 版,第一個陌生人回報:都打不開
系列文
一個 Vibe Coding 專案從原型到有人在用 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言