JAVASCRIPT V2 · ADVANCED · 129
Date / Intl / Time:時間不是「一個漂亮字串」
時間 bug 常不是語法錯,而是 domain model 錯:把 instant、local wall-clock time、timezone、duration、display format 混成同一件事。這一課先建立足以避免多數前端時間錯誤的安全模型。
Learning outcomes
- 區分 instant、timestamp、timezone、locale 與 duration。
- 安全使用 Date、Date.now、toISOString 與 Intl.DateTimeFormat。
- 理解 UTC instant 與 local display 的差異。
- 辨識 ambiguous parsing、timezone conversion 與 DST 風險。
- 將 storage/comparison 與 presentation formatting 分離。
1. Why now:同一個時間點可以有很多顯示方式
2026-10-06T14:30:00Z
Taipei:
2026-10-06 22:30
New York:
different local clock time
這些可以代表同一個 instant。顯示字串不同,不代表事件發生在不同瞬間。
2. Mental model:先分五個概念
Instant
→ timeline 上的一個點
Timestamp
→ instant 的機器表示
Timezone
→ instant ↔ local clock rules
Locale
→ 顯示語言/格式習慣
Duration
→ 兩個時間點之間的經過量
如果資料模型先分清楚,API、排序、顯示與報表就不容易互相污染。
3. Worked Example A:Date.now 與 Date instance
const nowMs =
Date.now();
const now =
new Date(nowMs);
console.log(
now.toISOString()
);
Date.now() 回毫秒 timestamp;Date object 封裝相同時間點並提供 parsing/formatting/getters。
4. ISO 8601 與 Z
const createdAt =
new Date(
"2026-10-06T14:30:00Z"
);
createdAt.toISOString();
// same instant in UTC form
Z 表示 UTC。API contract 若交換時間點,使用明確 offset/UTC 格式比模糊字串安全。
5. Worked Example B:Intl.DateTimeFormat 做 Presentation
const formatter =
new Intl.DateTimeFormat(
"zh-TW",
{
dateStyle: "medium",
timeStyle: "short",
timeZone: "Asia/Taipei"
}
);
formatter.format(
createdAt
);
不要在 storage layer 儲存「10/6 晚上 10:30」這種 locale display string,再期待它能可靠排序與交換。
6. Execution trace:同一 instant,不同 timezone display
instant:
2026-10-06T14:30:00Z
format timeZone=Asia/Taipei
→ local display A
format timeZone=UTC
→ local display B
instant did not change
Timezone formatting 改的是「如何投影到當地時鐘」,不是把事件本身移動到另一個 instant。
7. Duration:用差值,不要拿 Date 當 stopwatch UI model
const start =
performance.now();
// work
const elapsedMs =
performance.now()
- start;
如果你測量短期 elapsed duration,monotonic-style timing API 比 wall clock 更適合。Wall clock 可能被系統時間調整影響。
8. Worked Example C:Intl.NumberFormat 也是 Presentation Boundary
const currency =
new Intl.NumberFormat(
"zh-TW",
{
style: "currency",
currency: "TWD"
}
);
currency.format(1234.5);
和 DateTimeFormat 一樣:內部保存數值,UI 才根據 locale 格式化。
9. Common mistakes
A. 用 display string 排序
不同 locale 格式的字串 lexicographic order 不等於 chronological order。
B. 模糊 parsing
new Date("10/11/12")
這種輸入的語意不清,API contract 應避免。
C. 把 duration 當 local clock time
經過 90 分鐘不是「某地區 01:30」。
D. 假設所有一天都是固定 24 小時
涉及 daylight-saving transitions 的 timezone,local calendar arithmetic 可能不是單純毫秒加減。
10. Debug evidence:先印 canonical instant,再看 display
console.log({
raw,
iso: date.toISOString(),
timestamp: date.getTime(),
resolved:
formatter
.resolvedOptions()
});
遇到「時間差 8 小時」時,不要立刻手動 +8。先確認輸入是否含 offset、內部 instant 是什麼、formatter timeZone 是什麼。
11. Guided exercise:一個 Instant,兩種 Display
給定 ISO UTC timestamp,分別用 Asia/Taipei 與 UTC 顯示;證明 getTime() 沒有因 formatting 改變。
const instant =
new Date(
"2026-01-02T03:04:05Z"
);12. Independent exercise:Trip duration formatter
輸入 startMs/endMs,回傳純 duration object:
{
totalSeconds,
minutes,
seconds
}
不得用 DateTimeFormat 假裝 duration 是某個 clock time。
13. Challenge:API Contract Review
{
"meetingTime":
"2026-10-06 22:30"
}
指出這個 contract 缺哪些資訊。如果這是「某個絕對會議瞬間」,你會如何改?如果這是「每週二當地晚上 22:30」的 recurring wall-clock rule,又需要哪些不同資訊?
Project checkpoint:Grade Tracker Audit Timestamp
function stampUpdate(
student
) {
return {
...student,
updatedAt:
new Date()
.toISOString()
};
}
資料層保存 canonical timestamp;view 層才依使用者 locale/timezone 顯示。
14. Vocabulary / Summary
- instant:timeline 上的單一時間點。
- timestamp:machine-readable instant representation。
- timezone:local clock 與 UTC instant 的轉換規則。
- locale:語言、數字、日期顯示慣例。
- duration:經過時間量。
- presentation boundary:把 domain value 轉成使用者顯示格式的層。
15. Further reading
Knowledge check · Mastery
- 區分 instant / timezone / locale / duration。
- 解釋為何 display string 不該當 canonical storage。
- 完成 dual-timezone formatting 與 duration exercise。
- 完成頁面下方 Executable JS timezone tests。