讓我一起學(xué)習(xí)一個(gè)新的內(nèi)建對(duì)象:日期(date)。該對(duì)象存儲(chǔ)日期和時(shí)間,并提供了日期/時(shí)間的管理方法。
例如,我們可以使用它來存儲(chǔ)創(chuàng)建/修改時(shí)間,或者用來測(cè)量時(shí)間,再或者僅用來打印當(dāng)前時(shí)間。
一、創(chuàng)建
創(chuàng)建一個(gè)新的 Date 對(duì)象,只需要調(diào)用 new Date(),在調(diào)用時(shí)可以帶有下面這些參數(shù)之一:
new Date()
不帶參數(shù) —— 創(chuàng)建一個(gè)表示當(dāng)前日期和時(shí)間的 Date 對(duì)象:
let now = new Date(); alert( now ); // 顯示當(dāng)前的日期/時(shí)間
new Date(milliseconds)
創(chuàng)建一個(gè) Date 對(duì)象,其時(shí)間等于 1970-01-01 00:00:00 UTC+0 再過一毫秒(1/1000 秒)。
// 0 表示 01.01.1970 UTC+0 let Jan01_1970 = new Date(0); alert( Jan01_1970 ); // 現(xiàn)在增加 24 小時(shí),得到 02.01.1970 UTC+0 let Jan02_1970 = new Date(24 * 3600 * 1000); alert( Jan02_1970 );
傳入的整數(shù)參數(shù)代表的是自 1970-01-01 00:00:00 以來經(jīng)過的毫秒數(shù),該整數(shù)被稱為 時(shí)間戳。
這是一種日期的輕量級(jí)數(shù)字表示形式。我們通常使用 new Date(timestamp) 通過時(shí)間戳來創(chuàng)建日期,并可以使用 da() 將現(xiàn)有的 Date 對(duì)象轉(zhuǎn)化為時(shí)間戳(下文會(huì)講到)。
在 01.01.1970 之前的日期帶有負(fù)的時(shí)間戳,例如:
// 31 Dec 1969 let Dec31_1969 = new Date(-24 * 3600 * 1000); alert( Dec31_1969 );
new Date(datestring)
如果只有一個(gè)參數(shù),并且是字符串,那么它會(huì)被自動(dòng)解析。該算法與 Da 所使用的算法相同,我們將在下文中進(jìn)行介紹。
let date = new Date("2017-01-26"); alert(date); // 該時(shí)間未被設(shè)定,因此被假定為格林尼治標(biāo)準(zhǔn)時(shí)間(GMT)的午夜(midnight) // 并會(huì)根據(jù)你運(yùn)行代碼時(shí)的時(shí)區(qū)進(jìn)行調(diào)整 // 因此,結(jié)果可能是 // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) // 或 // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time)
new Date(year, month, date, hours, minutes, seconds, ms)
使用當(dāng)前時(shí)區(qū)中的給定組件創(chuàng)建日期。只有前兩個(gè)參數(shù)是必須的。
- year 必須是四位數(shù):2013 是合法的,98 是不合法的。
- month 計(jì)數(shù)從 0(一月)開始,到 11(十二月)結(jié)束。
- date 是當(dāng)月的具體某一天,如果缺失,則為默認(rèn)值 1。
- 如果 hours/minutes/seconds/ms 缺失,則均為默認(rèn)值 0。
例如:
new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00 new Date(2011, 0, 1); // 同樣,時(shí)分秒等均為默認(rèn)值 0
時(shí)間度量最大精確到 1 毫秒(1/1000 秒):
let date = new Date(2011, 0, 1, 2, 3, 4, 567); alert( date ); // 1.01.2011, 02:03:04.567
二、訪問日期組件
從 Date 對(duì)象中訪問年、月等信息有多種方式:
getFullYear()獲取年份(4 位數(shù))getMonth()獲取月份,從 0 到 11。getDate()獲取當(dāng)月的具體日期,從 1 到 31,這個(gè)方法名稱可能看起來有些令人疑惑。getHours(),getMinutes(),getSeconds(),getMilliseconds()獲取相應(yīng)的時(shí)間組件。
不是 getYear(),而是 getFullYear()
很多 JavaScript 引擎都實(shí)現(xiàn)了一個(gè)非標(biāo)準(zhǔn)化的方法 getYear()。不推薦使用這個(gè)方法。它有時(shí)候可能會(huì)返回 2 位的年份信息。永遠(yuǎn)都不要使用它。要獲取年份就使用 getFullYear()。
另外,我們還可以獲取一周中的第幾天:
getDay()獲取一周中的第幾天,從 0(星期日)到 6(星期六)。第一天始終是星期日,在某些國家可能不是這樣的習(xí)慣,但是這不能被改變。
以上的所有方法返回的組件都是基于當(dāng)?shù)貢r(shí)區(qū)的。
當(dāng)然,也有與當(dāng)?shù)貢r(shí)區(qū)的 UTC 對(duì)應(yīng)項(xiàng),它們會(huì)返回基于 UTC+0 時(shí)區(qū)的日、月、年等:getUTCFullYear(),getUTCMonth(),getUTCDay()。只需要在 "get" 之后插入 "UTC" 即可。
如果你當(dāng)?shù)貢r(shí)區(qū)相對(duì)于 UTC 有偏移,那么下面代碼會(huì)顯示不同的小時(shí)數(shù):
// 當(dāng)前日期 let date = new Date(); // 當(dāng)?shù)貢r(shí)區(qū)的小時(shí)數(shù) alert( da() ); // 在 UTC+0 時(shí)區(qū)的小時(shí)數(shù)(非夏令時(shí)的倫敦時(shí)間) alert( da() );
除了上述給定的方法,還有兩個(gè)沒有 UTC 變體的特殊方法:
getTime()
返回日期的時(shí)間戳 —— 從 1970-1-1 00:00:00 UTC+0 開始到現(xiàn)在所經(jīng)過的毫秒數(shù)。
getTimezoneOffset()
返回 UTC 與本地時(shí)區(qū)之間的時(shí)差,以分鐘為單位:
// 如果你在時(shí)區(qū) UTC-1,輸出 60 // 如果你在時(shí)區(qū) UTC+3,輸出 -180 alert( new Date().getTimezoneOffset() );
三、設(shè)置日期組件
下列方法可以設(shè)置日期/時(shí)間組件:
- 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 00:00:00 UTC+0 以來的毫秒數(shù)來設(shè)置整個(gè)日期)
以上方法除了 setTime() 都有 UTC 變體,例如:setUTCHours()。
我們可以看到,有些方法可以一次性設(shè)置多個(gè)組件,比如 setHours。未提及的組件不會(huì)被修改。
舉個(gè)例子:
let today = new Date(); (0); alert(today); // 日期依然是今天,但是小時(shí)數(shù)被改為了 0 (0, 0, 0, 0); alert(today); // 日期依然是今天,時(shí)間為 00:00:00。
四、自動(dòng)校準(zhǔn)(Autocorrection)
自動(dòng)校準(zhǔn) 是 Date 對(duì)象的一個(gè)非常方便的特性。我們可以設(shè)置超范圍的數(shù)值,它會(huì)自動(dòng)校準(zhǔn)。
舉個(gè)例子:
let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!? alert(date); // ……是 1st Feb 2013!
超出范圍的日期組件將會(huì)被自動(dòng)分配。
假設(shè)我們要在日期 “28 Feb 2016” 上加 2 天。結(jié)果可能是 “2 Mar” 或 “1 Mar”,因?yàn)榇嬖陂c年。但是我們不需要去考慮這些,只需要直接加 2 天,剩下的 Date 對(duì)象會(huì)幫我們處理:
let date = new Date(2016, 1, 28); da() + 2); alert( date ); // 1 Mar 2016
這個(gè)特性經(jīng)常被用來獲取給定時(shí)間段后的日期。例如,我們想獲取“現(xiàn)在 70 秒后”的日期:
let date = new Date(); da() + 70); alert( date ); // 顯示正確的日期信息
我們還可以設(shè)置 0 甚至可以設(shè)置負(fù)值。例如:
let date = new Date(2016, 0, 2); // 2016 年 1 月 2 日 da(1); // 設(shè)置為當(dāng)月的第一天 alert( date ); da(0); // 天數(shù)最小可以設(shè)置為 1,所以這里設(shè)置的是上一月的最后一天 alert( date ); // 31 Dec 2015
五、日期轉(zhuǎn)化為數(shù)字,日期差值
當(dāng) Date 對(duì)象被轉(zhuǎn)化為數(shù)字時(shí),得到的是對(duì)應(yīng)的時(shí)間戳,與使用 da() 的結(jié)果相同:
let date = new Date(); alert(+date); // 以毫秒為單位的數(shù)值,與使用 da() 的結(jié)果相同
有一個(gè)重要的副作用:日期可以相減,相減的結(jié)果是以毫秒為單位時(shí)間差。
這個(gè)作用可以用于時(shí)間測(cè)量:
let start = new Date(); // 開始測(cè)量時(shí)間 // do the job for (let i = 0; i < 100000; i++) { let doSomething = i * i * i; } let end = new Date(); // 結(jié)束測(cè)量時(shí)間 alert( `The loop took ${end - start} ms` );
六、Da()
如果我們僅僅想要測(cè)量時(shí)間間隔,我們不需要 Date 對(duì)象。
有一個(gè)特殊的方法 Da(),它會(huì)返回當(dāng)前的時(shí)間戳。
它相當(dāng)于 new Date().getTime(),但它不會(huì)創(chuàng)建中間的 Date 對(duì)象。因此它更快,而且不會(huì)對(duì)垃圾處理造成額外的壓力。
這種方法很多時(shí)候因?yàn)榉奖悖只蚴且蛐阅芊矫娴目紤]而被采用,例如使用 JavaScript 編寫游戲或其他的特殊應(yīng)用場(chǎng)景。
因此這樣做可能會(huì)更好:
let start = Da(); // 從 1 Jan 1970 至今的時(shí)間戳 // do the job for (let i = 0; i < 100000; i++) { let doSomething = i * i * i; } let end = Da(); // 完成 alert( `The loop took ${end - start} ms` ); // 相減的是時(shí)間戳,而不是日期
七、度量(Benchmarking)
如果我們想要為一個(gè)很耗 CPU 性能的函數(shù)提供一個(gè)可靠的度量(benchmark),我們應(yīng)該小心一點(diǎn)。
例如,我們想判斷兩個(gè)計(jì)算日期差值的函數(shù):哪個(gè)更快?
這種性能測(cè)量通常稱為“度量(benchmark)”。
// 我們有 date1 和 date2,哪個(gè)函數(shù)會(huì)更快地返回兩者的時(shí)間差? function diffSubtract(date1, date2) { return date2 - date1; } // or function diffGetTime(date1, date2) { return da() - da(); }
這兩個(gè)函數(shù)做的事情完全相同,但是其中一個(gè)函數(shù)使用顯性的 da() 來獲取毫秒形式的日期,另一個(gè)則依賴于“日期 — 數(shù)字”的轉(zhuǎn)換。它們的結(jié)果是一樣的。
那么,哪個(gè)更快呢?
首先想到的方法可能是連續(xù)運(yùn)行它們很多次,并計(jì)算時(shí)間差。就我們的例子而言,函數(shù)非常簡(jiǎn)單,所以我們必須執(zhí)行至少 100000 次。
讓我們開始測(cè)量:
function diffSubtract(date1, date2) { return date2 - date1; } function diffGetTime(date1, date2) { return da() - da(); } function bench(f) { let date1 = new Date(0); let date2 = new Date(); let start = Da(); for (let i = 0; i < 100000; i++) f(date1, date2); return Da() - start; } alert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' ); alert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );
哇!使用 getTime() 這種方式快得多!原因是它沒有類型轉(zhuǎn)化,這樣對(duì)引擎優(yōu)化來說更加簡(jiǎn)單。
好,我們得到了結(jié)論,但是這并不是一個(gè)很好的度量的例子。
想象一下當(dāng)運(yùn)行 bench(diffSubtract) 的同時(shí),CPU 還在并行處理其他事務(wù),并且這也會(huì)占用資源。然而,運(yùn)行 bench(diffGetTime) 的時(shí)候,并行處理的事務(wù)完成了。
這是對(duì)于現(xiàn)代多進(jìn)程操作系統(tǒng)來說的一個(gè)非常真實(shí)的場(chǎng)景。
結(jié)果就是,第一個(gè)函數(shù)相比于第二個(gè)函數(shù),缺少 CPU 資源。這可能導(dǎo)致錯(cuò)誤的結(jié)論。
為了得到更加可靠的度量,整個(gè)度量測(cè)試包應(yīng)該重新運(yùn)行多次。
例如,像下面的代碼這樣:
function diffSubtract(date1, date2) { return date2 - date1; } function diffGetTime(date1, date2) { return da() - da(); } function bench(f) { let date1 = new Date(0); let date2 = new Date(); let start = Da(); for (let i = 0; i < 100000; i++) f(date1, date2); return Da() - start; } let time1 = 0; let time2 = 0; // 交替運(yùn)行 bench(upperSlice) 和 bench(upperLoop) 各 10 次 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 );
現(xiàn)代的 JavaScript 引擎的先進(jìn)優(yōu)化策略只對(duì)執(zhí)行很多次的 “hot code” 有效(對(duì)于執(zhí)行很少次數(shù)的代碼沒有必要優(yōu)化)。因此,在上面的例子中,第一次執(zhí)行的優(yōu)化程度不高。我們可能需要增加一個(gè)升溫步驟:
// 在主循環(huán)中增加“升溫”環(huán)節(jié) bench(diffSubtract); bench(diffGetTime); // 開始度量 for (let i = 0; i < 10; i++) { time1 += bench(diffSubtract); time2 += bench(diffGetTime); }
進(jìn)行微度量測(cè)試時(shí)要小心
現(xiàn)代的 JavaScript 引擎執(zhí)行了很多優(yōu)化。與“正常使用”相比,它們可能會(huì)改變“人為測(cè)試”的結(jié)果,特別是在我們對(duì)很細(xì)微的東西進(jìn)行度量測(cè)試時(shí),例如 operator 的工作方式或內(nèi)建函數(shù)。因此,如果你想好好了解一下性能,請(qǐng)學(xué)習(xí) JavaScript 引擎的工作原理。在那之后,你可能再也不需要微度量了。
關(guān)于 V8 引擎的大量文章,可以在 找到。
八、對(duì)一個(gè)字符串使用 Da
Da(str) 方法可以從一個(gè)字符串中讀取日期。
字符串的格式應(yīng)該為:YYYY-MM-DDTHH:mm:ss.sssZ,其中:
- YYYY-MM-DD —— 日期:年-月-日。
- 字符 "T" 是一個(gè)分隔符。
- HH:mm:ss.sss —— 時(shí)間:小時(shí),分鐘,秒,毫秒。
- 可選字符 'Z' 為 +-hh:mm 格式的時(shí)區(qū)。單個(gè)字符 Z 代表 UTC+0 時(shí)區(qū)。
簡(jiǎn)短形式也是可以的,比如 YYYY-MM-DD 或 YYYY-MM,甚至可以是 YYYY。
Da(str) 調(diào)用會(huì)解析給定格式的字符串,并返回時(shí)間戳(自 1970-01-01 00:00:00 起所經(jīng)過的毫秒數(shù))。如果給定字符串的格式不正確,則返回 NaN。
舉個(gè)例子:
let ms = Da('2012-01-26T13:51:50.417-07:00'); alert(ms); // 17 (時(shí)間戳)
我們可以通過時(shí)間戳來立即創(chuàng)建一個(gè) new Date 對(duì)象:
let date = new Date( Da('2012-01-26T13:51:50.417-07:00') ); alert(date);
九、總結(jié)
- 在 JavaScript 中,日期和時(shí)間使用 Date 對(duì)象來表示。我們不能只創(chuàng)建日期,或者只創(chuàng)建時(shí)間,Date 對(duì)象總是同時(shí)創(chuàng)建兩者。
- 月份從 0 開始計(jì)數(shù)(對(duì),一月是 0)。
- 一周中的某一天 getDay() 同樣從 0 開始計(jì)算(0 代表星期日)。
- 當(dāng)設(shè)置了超出范圍的組件時(shí),Date 會(huì)進(jìn)行自我校準(zhǔn)。這一點(diǎn)對(duì)于日/月/小時(shí)的加減很有用。
- 日期可以相減,得到的是以毫秒表示的兩者的差值。因?yàn)楫?dāng) Date 被轉(zhuǎn)換為數(shù)字時(shí),Date 對(duì)象會(huì)被轉(zhuǎn)換為時(shí)間戳。
- 使用 Da() 可以更快地獲取當(dāng)前時(shí)間的時(shí)間戳。
和其他系統(tǒng)不同,JavaScript 中時(shí)間戳以毫秒為單位,而不是秒。
有時(shí)我們需要更加精準(zhǔn)的時(shí)間度量。JavaScript 自身并沒有測(cè)量微秒的方法(百萬分之一秒),但大多數(shù)運(yùn)行環(huán)境會(huì)提供。例如:瀏覽器有 () 方法來給出從頁面加載開始的以毫秒為單位的微秒數(shù)(精確到毫秒的小數(shù)點(diǎn)后三位):
alert(`Loading started ${()}ms ago`); // 類似于 "Loading started 34731.26000000001ms ago" // .26 表示的是微秒(260 微秒) // 小數(shù)點(diǎn)后超過 3 位的數(shù)字是精度錯(cuò)誤,只有前三位數(shù)字是正確的
Node.js 有 microtime 模塊以及其他方法。從技術(shù)上講,幾乎所有的設(shè)備和環(huán)境都允許獲取更高精度的數(shù)值,只是不是通過 Date 對(duì)象。
1.《js如何輸出標(biāo)準(zhǔn)時(shí)間 js獲取中國標(biāo)準(zhǔn)時(shí)間》援引自互聯(lián)網(wǎng),旨在傳遞更多網(wǎng)絡(luò)信息知識(shí),僅代表作者本人觀點(diǎn),與本網(wǎng)站無關(guān),侵刪請(qǐng)聯(lián)系頁腳下方聯(lián)系方式。
2.《js如何輸出標(biāo)準(zhǔn)時(shí)間 js獲取中國標(biāo)準(zhǔn)時(shí)間》僅供讀者參考,本網(wǎng)站未對(duì)該內(nèi)容進(jìn)行證實(shí),對(duì)其原創(chuàng)性、真實(shí)性、完整性、及時(shí)性不作任何保證。
3.文章轉(zhuǎn)載時(shí)請(qǐng)保留本站內(nèi)容來源地址,http://f99ss.com/keji/3218023.html