Learning Objectives
- 用 new Date 建立日期,並用 getFullYear、getMonth、getDate、getDay 取得年月日與星期
- 說明月份從 0 開始,以及時間戳記(從 1970 年起算的毫秒數)
- 用 setTimeout 延遲執行一次,用 clearTimeout 取消
- 用 setInterval 重複執行,用 clearInterval 停止
- 說明計時器的 callback 要等同步程式跑完才會執行
Date:處理日期與時間
JavaScript 用 Date 物件表示一個時間點。建立的方式:
| 寫法 | 得到 |
|---|---|
new Date() |
現在 |
new Date(2019, 11, 25) |
2019 年 12 月 25 日 0 點(本地時間):依序是年、月、日、時、分、秒、毫秒,沒寫的是 0(日是 1) |
new Date(0) |
1970-01-01 00:00:00 UTC 之後 0 毫秒 |
new Date('2019-12-25T10:30:00') |
依字串解析;格式建議用 ISO 格式(年-月-日T時:分:秒) |
月份從 0 開始:0 是一月、11 是十二月。這是最常出錯的地方,顯示給人看時記得 getMonth() + 1。
超出範圍的值會自動進位:new Date(2019, 12, 25) 是 2020 年 1 月 25 日。
| 方法 | 回傳 |
|---|---|
getFullYear() |
四位數的年 |
getMonth() |
月,0~11 |
getDate() |
日,1~31 |
getDay() |
星期幾,0 是星期日、6 是星期六 |
getHours()、getMinutes()、getSeconds() |
時、分、秒 |
setFullYear(y)、setDate(d)、setHours(h)… |
修改;超出範圍會自動進位到下個月或下一年 |
這些方法使用瀏覽器所在地的時區(本地時間)。另外有一組 getUTCFullYear()、getUTCHours()… 使用世界標準時間(UTC)。
建立日期與取得年月日
new Date(2019, 11, 25) 的 11 代表十二月。注意第 3 行印出的月份是 11,第 7 行組字串時要加 1 才是我們習慣的 12 月。
時間戳記
電腦內部用一個數字記錄 Date:從 1970 年 1 月 1 日 00:00:00 UTC 到這個時間點的毫秒數,叫做時間戳記(timestamp)。
| 寫法 | 意思 |
|---|---|
Date.now() |
現在的時間戳記 |
d.getTime() |
這個 Date 的時間戳記 |
Date.UTC(2026, 9, 29) |
指定 UTC 時間的時間戳記(月份一樣從 0 開始) |
d2 - d1 |
兩個 Date 相減,得到相差的毫秒數 |
d.toISOString() |
轉成 ISO 格式的 UTC 字串,例如 2026-10-29T06:30:00.000Z |
用時間戳記可以計算經過的時間:一天是 1000 * 60 * 60 * 24 毫秒。
Date 是物件,=== 比的是不是同一個物件;比較兩個時間點要比 getTime()。
逐步執行動畫裡的「現在」是固定的虛擬時間(台北時間 2026-10-29 14:30,也就是 UTC 06:30),所以每次播放結果都一樣。 範例用
toISOString()以 UTC 顯示時間,不管你在哪個時區看都相同。在真正的瀏覽器裡,new Date()是你電腦的現在時間。
時間戳記與時間的計算
第 4 行用兩個時間戳記相減,算出 10 月 1 日到 29 日相差幾毫秒,再換成天數。最後一段兩個 Date 表示同一個時間,=== 卻是 false。
想一想:Date
下面的程式會印出什麼?
const d = new Date(2026, 2, 8);
console.log(d.getMonth(), d.getDate());
下面的程式會印出什麼?
const d = new Date(2026, 0, 31);
d.setDate(d.getDate() + 1);
console.log(d.getMonth() + 1, d.getDate());
getDay() 回傳 0 代表什麼?
setTimeout:過一段時間執行一次
const id = setTimeout(callback, 毫秒, 要傳給 callback 的引數…);
clearTimeout(id); // 時間還沒到之前取消
setTimeout設定好計時器之後馬上結束,不會停在那裡等;程式繼續往下執行- 時間到了,
callback才被執行,而且只執行一次 - 回傳一個編號(正整數),把它交給
clearTimeout可以取消還沒執行的計時器 - 延遲寫
0也不會立刻執行:callback 一定要等目前的同步程式全部跑完才輪得到它
最後一點是 JavaScript 執行模型的核心,叫做事件迴圈(event loop):瀏覽器在背景計時,時間到了就把 callback 放進「工作佇列」排隊; 等呼叫堆疊上的程式都執行完,事件迴圈才從佇列裡拿出下一件工作來執行。下面的小工具一步一步示範這個過程。
投影片也寫了字串的寫法 setTimeout("alert('Hello')", 3000):它會像 eval 一樣執行字串,容易出錯也有安全疑慮,請一律傳入函式。
setTimeout 的執行順序與 clearTimeout
程式設定了三個計時器:3 秒、2 秒、0 毫秒。同步程式(第 2~15 行)先全部跑完,計時器的 callback 才依時間順序執行。2 秒的計時器取消了 3 秒的那一個,所以「Hello」不會出現。
用迴圈設定多個計時器
投影片的練習:每秒印出 0、1、2、3。迴圈本身一瞬間就跑完了,只是設定了延遲 0、1000、2000、3000 毫秒的四個計時器。每個 callback 印出虛擬時間的時、分、秒(UTC):每次相差一秒。
setInterval:每隔一段時間執行
const id = setInterval(callback, 毫秒);
clearInterval(id); // 停止
setInterval 每隔指定的時間就執行一次 callback,直到 clearInterval 才停止;忘了清除,它會一直執行下去。
另一種做法是在 setTimeout 的 callback 裡再設定下一次 setTimeout(投影片 Lab01 的作法)。兩者的差別:
setInterval 依固定的間隔排進佇列,不管上一次花了多久;鏈式的 setTimeout 等這一次執行完才開始算下一次的時間。
計時器不保證準時:間隔是「最少」要等多久,如果這時候還有其他程式在執行,callback 就要繼續排隊。
所以碼表、倒數這類要準確顯示經過時間的程式,應該記下開始的時間,每次用 Date.now() - 開始時間 計算,而不是單純數執行了幾次。
逐步執行動畫的時間是虛擬的:計時器不會真的等待,而是依時間先後立刻執行,所以 1 秒、2 秒的計時器一下子就播完了。 範例印出的時間是虛擬時鐘的時間。下方的網頁沙箱則是真的瀏覽器,會照實際時間執行。
setInterval 與 clearInterval
同步程式先印出「計時開始」。之後 callback 每一秒執行一次,第 3 次時呼叫 clearInterval(id),就不再執行了。
用 setTimeout 做出重複
tick 每次執行時,如果還沒滿 3 次,就再設定一次一秒後的 setTimeout(tick, 1000)。
想一想:計時器
下面的程式會印出什麼?
console.log('A');
setTimeout(() => console.log('B'), 0);
console.log('C');
下面的程式會印出什麼?
const t = setTimeout(() => console.log('時間到'), 1000);
clearTimeout(t);
console.log('結束');
下面的程式會印出什麼?
let n = 0;
const id = setInterval(() => {
n += 2;
if (n >= 6) {
clearInterval(id);
console.log(n);
}
}, 500);
實作:日期格式化
完成 formatDate(d):回傳「年/月/日 星期幾」的字串,月與日都補成兩位數(用字串的 padStart(2, '0'))。例如 2026 年 1 月 5 日要顯示 2026/01/05 星期一。
重點精華回顧
new Date()是現在,new Date(年, 月, 日, …)指定時間;月份從 0 開始,getDay()的 0 是星期日。- 超出範圍的值會自動進位:
setDate(getDate() + 5)可以跨月。 - 時間戳記是從 1970-01-01 00:00:00 UTC 起算的毫秒數:
Date.now()、getTime();兩個 Date 相減得到相差的毫秒。 setTimeout(fn, ms)過一段時間執行一次,clearTimeout(id)取消;setInterval(fn, ms)重複執行,clearInterval(id)停止。- 設定計時器不會讓程式停下來等;callback 要等目前的同步程式跑完,由事件迴圈從工作佇列取出執行,即使延遲是 0。
- 計時器不保證準時;要準確計算經過的時間,用
Date.now()相減。