讓我們認識一個新的內建物件:Date。它儲存日期、時間,並提供日期/時間管理的方法。
例如,我們可以使用它來儲存建立/修改時間,測量時間,或僅列印出目前的日期。
建立
要建立新的 Date
物件,請使用下列其中一個引數呼叫 new Date()
new Date()
-
沒有引數 – 為目前的日期和時間建立一個
Date
物件let now = new Date(); alert( now ); // shows current date/time
new Date(milliseconds)
-
建立一個
Date
物件,其時間等於自 1970 年 1 月 1 日 UTC+0 之後經過的毫秒數(1/1000 秒)。// 0 means 01.01.1970 UTC+0 let Jan01_1970 = new Date(0); alert( Jan01_1970 ); // now add 24 hours, get 02.01.1970 UTC+0 let Jan02_1970 = new Date(24 * 3600 * 1000); alert( Jan02_1970 );
自 1970 年初以來經過的毫秒數的整數稱為時間戳記。
它是日期的輕量級數字表示法。我們可以使用 `new Date(timestamp)` 隨時從時間戳記建立日期,並使用 `date.getTime()` 方法將現有的 `Date` 物件轉換為時間戳記(見下文)。
1970 年 01 月 01 日之前的日期具有負時間戳記,例如
// 31 Dec 1969 let Dec31_1969 = new Date(-24 * 3600 * 1000); alert( Dec31_1969 );
new Date(datestring)
-
如果只有一個引數,且為字串,則會自動進行剖析。演算法與 `Date.parse` 使用的相同,我們稍後會介紹。
let date = new Date("2017-01-26"); alert(date); // The time is not set, so it's assumed to be midnight GMT and // is adjusted according to the timezone the code is run in // So the result could be // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) // or // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time)
new Date(year, month, date, hours, minutes, seconds, ms)
-
使用當地時區的指定元件建立日期。只有前兩個引數是必要的。
year
應有 4 個數字。為了相容性,也接受 2 個數字並視為19xx
,例如 98 在這裡與1998
相同,但強烈建議始終使用 4 個數字。month
計數從0
(1 月)開始,到11
(12 月)結束。date
參數實際上是該月份的天數,如果不存在,則假設為1
。- 如果
hours/minutes/seconds/ms
不存在,則假設它們等於0
。
例如
new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00 new Date(2011, 0, 1); // the same, hours etc are 0 by default
最大精度為 1 毫秒(1/1000 秒)
let date = new Date(2011, 0, 1, 2, 3, 4, 567); alert( date ); // 1.01.2011, 02:03:04.567
存取日期元件
有方法可以從 Date
物件存取年份、月份等
- getFullYear()
- 取得年份(4 個數字)
- getMonth()
- 取得月份,從 0 到 11。
- getDate()
- 取得該月份的天數,從 1 到 31,方法名稱看起來有點奇怪。
- getHours()、getMinutes()、getSeconds()、getMilliseconds()
- 取得對應的時間元件。
getYear()
,而是 getFullYear()
許多 JavaScript 引擎實作非標準方法 getYear()
。此方法已棄用。它有時會傳回 2 位數的年份。請不要使用它。年份有 getFullYear()
。
此外,我們可以取得星期幾
- getDay()
- 取得星期幾,從
0
(星期日)到6
(星期六)。星期日永遠是第一天,在某些國家並非如此,但無法變更。
以上所有方法都傳回相對於當地時區的元件。
還有它們的 UTC 對應版本,會傳回 UTC+0 時區的日期、月份、年份等:getUTCFullYear()、getUTCMonth()、getUTCDay()。只要在 "get"
後面插入 "UTC"
即可。
如果您的當地時區相對於 UTC 有所偏移,那麼以下程式碼會顯示不同的時間
// current date
let date = new Date();
// the hour in your current time zone
alert( date.getHours() );
// the hour in UTC+0 time zone (London time without daylight savings)
alert( date.getUTCHours() );
除了這些方法之外,還有兩個特殊的方法沒有 UTC 變體
- getTime()
-
傳回日期的時間戳記,即自 1970 年 1 月 1 日 UTC+0 以來經過的毫秒數。
- getTimezoneOffset()
-
傳回 UTC 和當地時區的差,單位為分鐘
// if you are in timezone UTC-1, outputs 60 // if you are in timezone UTC+3, outputs -180 alert( new Date().getTimezoneOffset() );
設定日期元件
以下方法允許設定日期/時間元件
setFullYear(year, [month], [date])
setMonth(month, [date])
setDate(date)
setHours(hour, [min], [sec], [ms])
setMinutes(min, [sec], [ms])
setSeconds(sec, [ms])
setMilliseconds(ms)
setTime(milliseconds)
(根據自 1970 年 01 月 01 日 UTC 以來的毫秒數設定整個日期)
除了 setTime()
之外,每個方法都有 UTC 變體,例如:setUTCHours()
。
正如我們所見,有些方法可以一次設定多個元件,例如 setHours
。未提及的元件不會被修改。
例如
let today = new Date();
today.setHours(0);
alert(today); // still today, but the hour is changed to 0
today.setHours(0, 0, 0, 0);
alert(today); // still today, now 00:00:00 sharp.
自動校正
自動校正 是 Date
物件非常方便的功能。我們可以設定超出範圍的值,它會自動調整自身。
例如
let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!?
alert(date); // ...is 1st Feb 2013!
超出範圍的日期元件會自動分配。
假設我們需要將「2016 年 2 月 28 日」的日期增加 2 天。如果是閏年,它可能是「3 月 2 日」或「3 月 1 日」。我們不必考慮這些。只要加上 2 天即可。Date
物件會處理其餘部分
let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);
alert( date ); // 1 Mar 2016
此功能通常用於取得特定時間段後的日期。例如,讓我們取得「現在之後 70 秒」的日期
let date = new Date();
date.setSeconds(date.getSeconds() + 70);
alert( date ); // shows the correct date
我們也可以設定零或甚至負值。例如
let date = new Date(2016, 0, 2); // 2 Jan 2016
date.setDate(1); // set day 1 of month
alert( date );
date.setDate(0); // min day is 1, so the last day of the previous month is assumed
alert( date ); // 31 Dec 2015
日期轉數字、日期差
當 Date
物件轉換為數字時,它會變成與 date.getTime()
相同的時間戳記
let date = new Date();
alert(+date); // the number of milliseconds, same as date.getTime()
重要的副作用:可以減去日期,結果是它們的毫秒差。
這可以用於時間測量
let start = new Date(); // start measuring time
// do the job
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = new Date(); // end measuring time
alert( `The loop took ${end - start} ms` );
Date.now()
如果我們只想要測量時間,我們不需要Date
物件。
有一個特殊的方法Date.now()
會傳回目前的 timestamp。
它的語意等同於new Date().getTime()
,但它不會建立一個中間的Date
物件。所以它比較快,而且不會對垃圾回收造成壓力。
它大多用於方便性或效能很重要的時候,例如 JavaScript 遊戲或其他特殊應用程式。
所以這或許比較好
let start = Date.now(); // milliseconds count from 1 Jan 1970
// do the job
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = Date.now(); // done
alert( `The loop took ${end - start} ms` ); // subtract numbers, not dates
效能測試
如果我們想要一個可靠的 CPU 密集函式的效能測試,我們應該小心。
例如,我們來測量兩個計算兩個日期之間差值的函式:哪一個比較快?
這種效能測量通常稱為「效能測試」。
// we have date1 and date2, which function faster returns their difference in ms?
function diffSubtract(date1, date2) {
return date2 - date1;
}
// or
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
這兩個函式做的事情完全一樣,但其中一個使用明確的date.getTime()
取得日期的毫秒數,另一個則依賴日期轉數字的轉換。它們的結果總是相同。
所以,哪一個比較快?
第一個想法可能是連續執行它們很多次,然後測量時間差。對於我們的案例,函式非常簡單,所以我們必須執行至少 100000 次。
讓我們測量
function diffSubtract(date1, date2) {
return date2 - date1;
}
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
function bench(f) {
let date1 = new Date(0);
let date2 = new Date();
let start = Date.now();
for (let i = 0; i < 100000; i++) f(date1, date2);
return Date.now() - start;
}
alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );
alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );
哇!使用getTime()
快很多!這是因為沒有類型轉換,引擎比較容易最佳化。
好,我們有了一些東西。但這還不是一個好的效能測試。
想像一下在執行bench(diffSubtract)
的時候,CPU 正在平行做一些事情,而且它佔用了資源。而等到執行bench(diffGetTime)
的時候,那個工作已經完成了。
對於現代的多處理器作業系統來說,這是相當實際的場景。
結果是,第一個效能測試會比第二個效能測試有更少的 CPU 資源。這可能會導致錯誤的結果。
為了更可靠的效能測試,應該多次重新執行整組效能測試。
例如,像這樣
function diffSubtract(date1, date2) {
return date2 - date1;
}
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
function bench(f) {
let date1 = new Date(0);
let date2 = new Date();
let start = Date.now();
for (let i = 0; i < 100000; i++) f(date1, date2);
return Date.now() - start;
}
let time1 = 0;
let time2 = 0;
// run bench(diffSubtract) and bench(diffGetTime) each 10 times alternating
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
alert( 'Total time for diffSubtract: ' + time1 );
alert( 'Total time for diffGetTime: ' + time2 );
現代的 JavaScript 引擎只會對執行很多次的「熱門程式碼」套用進階最佳化(不需要最佳化很少執行的東西)。所以,在上面的範例中,第一次執行並沒有經過良好的最佳化。我們可能會想要加入熱身執行
// added for "heating up" prior to the main loop
bench(diffSubtract);
bench(diffGetTime);
// now benchmark
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
現代的 JavaScript 引擎執行許多最佳化。它們可能會調整「人工測試」的結果,與「正常使用」相比,特別是當我們測試非常小的東西時,例如運算子如何運作,或內建函式。所以如果你真的想要了解效能,請研究 JavaScript 引擎如何運作。然後你可能根本不需要微效能測試。
可以在 https://mrale.ph 找到關於 V8 的大量文章。
從字串解析 Date.parse
方法 Date.parse(str) 可以從字串讀取日期。
字串格式應為:YYYY-MM-DDTHH:mm:ss.sssZ
,其中
YYYY-MM-DD
– 是日期:年-月-日。- 字元
"T"
用作分隔符。 HH:mm:ss.sss
– 是時間:小時、分鐘、秒和毫秒。- 選用的
'Z'
部分表示時區,格式為+-hh:mm
。單一字母Z
表示 UTC+0。
也可以使用較短的變體,例如 YYYY-MM-DD
或 YYYY-MM
甚至 YYYY
。
呼叫 Date.parse(str)
會以指定的格式分析字串,並傳回時間戳記(從 1970 年 1 月 1 日 UTC+0 開始的毫秒數)。如果格式無效,則傳回 NaN
。
例如
let ms = Date.parse('2012-01-26T13:51:50.417-07:00');
alert(ms); // 1327611110417 (timestamp)
我們可以立即從時間戳記建立新的 Date
物件
let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );
alert(date);
摘要
- JavaScript 中的日期和時間以 Date 物件表示。我們無法建立「只有日期」或「只有時間」:
Date
物件總是同時包含兩者。 - 月份從 0 開始計算(是的,一月是 0 月)。
getDay()
中的星期幾也從 0 開始計算(星期日)。- 當設定超出範圍的組成時,
Date
會自動修正。這對於加/減天數/月份/小時很有用。 - 可以減去日期,得到它們的毫秒差。這是因為
Date
在轉換為數字時會變成時間戳記。 - 使用
Date.now()
快速取得目前的時間戳記。
請注意,與許多其他系統不同,JavaScript 中的時間戳記以毫秒為單位,而不是秒。
有時我們需要更精確的時間測量。JavaScript 本身沒有辦法以微秒(一秒的百萬分之一)為單位測量時間,但大多數環境都提供此功能。例如,瀏覽器具有 performance.now(),它會提供從頁面載入開始的毫秒數,並具有微秒精度(小數點後 3 位數)
alert(`Loading started ${performance.now()}ms ago`);
// Something like: "Loading started 34731.26000000001ms ago"
// .26 is microseconds (260 microseconds)
// more than 3 digits after the decimal point are precision errors, only the first 3 are correct
Node.js 有 microtime
模組和其他方法。技術上來說,幾乎所有裝置和環境都可以獲得更高的精度,只是不在 Date
中。
留言