天天看點

JavaScript深入了解之Date類型詳解

JavaScript深入了解之Date類型詳解

寫在前面

Date 類型平常用得不是很多,但一用到,對它的使用就感到不是很熟悉,每次都是強行百度一波,可以看出自己的基礎不是很牢。是以最近決定靜下心來好好回顧一下以前自己忽視的基礎,下面是我對 Date 類型的一些總結和看法。

UTC 和 GMT 及 中原標準時間的關系

在介紹Date類型前,我們先來了解一下 UTC 和 GMT 及 中原標準時間的關系。

  • GMT 即「格林威治标準時間」(Greenwich Mean Time,簡稱G.M.T.),指位于英國倫敦郊區的皇家格林威治天文台的标準時間,因為本初子午線被定義為通過那裡的經線。然而由于地球的不規則自轉,導緻GMT時間有誤差,是以目前已不被當作标準時間使用。
  • UTC 是最主要的世界時間标準,是經過平均太陽時(以格林威治時間GMT為準)、地軸運動修正後的新時标以及以「秒」為機關的國際原子時所綜合精算而成的時間。UTC 比 GMT 來得更加精準。其誤內插補點必須保持在0.9秒以内,若大于0.9秒則由位于巴黎的國際地球自轉事務中央局釋出閏秒,使 UTC 與地球自轉周期一緻。不過日常使用中,GMT 與 UTC 的功能與精确度是沒有差别的,我們在文章中提到的 GMT 時間與 UTC 時間是一樣的。
  • GMT = UTC
  • 因為時區的問題中原標準時間和UTC時間有這樣的關系 ​

    ​UTC + 8 = 中原標準時間​

    ​ , 這個公式有助于我們後面了解 Date 類型為什麼在不同方法下的轉換結果不同。

定義

ECMAScript 中的 Date 類型是在早期 Java 中的 java.util.Date 類基礎上建構的。為此 Date 類型使用自 UTC ( Coordinated Universal Time, 國際協調時間)1970年1月1日午夜(零時)開始經過的毫秒數來儲存日期。在使用這種資料存儲格式的條件下,Date()類型儲存的日期能夠精确到1970年1月1日之前或之後的100 000 000年。

我們可以這樣了解,我們建立的一個 Date 對象中儲存有一個 value,這個 value 的大小是從 UTC 時間1970年1月1日午夜至指定時間經過的毫秒數的大小。

這個值其實就是我們經常使用到的時間戳,需要注意的是js内的時間戳指的是指定時間到1970年1月1日00:00:00 UTC對應的毫秒數,和unix時間戳不是一個概念,後者表示秒數,差了1000倍。是以我們在轉換時經常會遇到精度丢失的問題(暫時采用這種說法)。

建立方式

要建立一個日期對象,使用 new 操作符和 Date 構造函數即可,如下所示。

​var now = new Date(); // 獲得目前時間​

在調用 Date 構造函數而不傳遞參數的情況下,新建立的對象自動擷取目前日期和時間。如果想要根據指定的日期和時間建立對象,必須傳入該日期的毫秒數(即從 UTC 時間1970年1月1日午夜至指定時間經過的毫秒數。)聽起來是不是有點頭大,難道我們還要自己計算好毫秒數才能建立相應的時間對象嗎?這樣豈不是太麻煩了?

Date.parse() 和 Date.UTC()

針對上面的問題ECMAScript提供了兩個方法 Date.parse() 和 Date.UTC() ,以此來簡化這一計算過程。它們會根據我們傳入的參數來自動計算出毫秒數的大小。下面我們來分别介紹一下這兩個方法。

Date.parse()

Date.parse()方法接受一個表示日期的字元串參數,然後嘗試根據這個字元串傳回相應的毫秒數,如果傳入的字元串不能表示将日期則傳回NaN。因為ECMA-262沒有定義這個方法應該支援那種日期格式,是以這個方法的行為通常是因地區而異。例如将地區設定為美國的浏覽器通常都接受下列日期格式:

  • “月/日/年”,如6/13/2004;
  • “英文月名日,年”,如January12,2004;
  • “英文星期幾 英文月名 日 年 時:分:秒 時區”,如Tue May 25 2004 00:00:00 GMT-0700。
  • ISO 8601擴充格式YYYY-MM-DDTHH:mm:ss.sssZ(例如2004-05-25T00:00:00)。隻有相容ECMAScript 5的實作支援這種格式。

例如,要為2004年5月25日建立一個日期對象,可以使用下面的代碼:

​var someDate = new Date(Date.parse("May 25, 2004"));​

Date.UTC()

Date.UTC()方法同樣也傳回表示日期的毫秒數。但它需要的參數不是字元串,它的參數分别是年份,基于0的月份(0到11),日(1到31),小時(0到23),分鐘,秒以及毫秒數。這些參數裡邊隻有前兩個參數是必需的,如果沒有提供日值,則預設日值為1,其餘參數未指定則預設為0。如下面的例子所示:

​//GMT時間2000年1月1日午夜零時:​

​​

​var y2k = new Date(Date.UTC(2000, 0));​

​​

​​

​//GMT時間2005年5月5日下午5:55:55:​

​​

​var allFives = new Date(Date.UTC(2005, 4, 5, 17, 55, 55));​

注意

  • UTC 日期指的是在沒有時區偏差的情況下(将日期轉換為GMT時間)的日期值。Date.parse() 方法是基于本地時區建立的,而 Date.UTC() 方法是基于無時區偏差建立的。是以如果我們對兩個方法傳入相同的時間,我們會發現 Date.parse() 方法得到的毫秒數相對于 Date.UTC() 方法得到的毫秒數會多八個小時的毫秒數(這裡的本地時區指的是中原標準時間)。​

​//假設我們傳入相同的時間2018年3月18日​

​​

​​

​Date.parse("3/18/2018"); // 1521302400000​

​​

​​

​Date.UTC(2018,2,18); //1521331200000​

​​

​​

​// 1521302400000 - 1521331200000 = 28800000 = 8 x 60 x 60 x 1000​

  • 如果我們輸入的日期值超過了正常的範圍,在不同的浏覽器中的會有不同的處理方式。例如在解析​

    ​"January 32,2007"​

    ​時,有的浏覽器會将其解析為​

    ​"February 1,2007"​

    ​。而Opera浏覽器則傾向于插入目前月份的目前日期值,傳回​

    ​"January 目前日期值,2007"​

    ​。
  • 其實我們沒有必要在建立一個 Date 對象的時候顯式調用 Date.parse() 和 Date.UTC() 方法,因為将相應的參數傳入構造函數後,它會根據參數的類型在背景自動調用Date.parse() 或 Date.UTC() 方法,這樣得到日期和時間都是基于本地時區的,就算你存入的參數類型是 Date.UTC() 方法所需的參數類型,最後得到的結果還是基于本地時區的結果。

Date.now()

ES5添加了Date.now()方法,用來傳回表示調用這個方法時的日期和時間的毫秒數。這個方法可以用來分析函數的運作時間,如下。

​// 取得開始時間​

​​

​var start = Date.now();​

​​

​​

​// 調用函數​

​​

​doSomething();​

​​

​​

​// 擷取結束時間​

​​

​var end = Date.now();​

​​

​​

​// 得到函數運作時間​

​​

​var​

在不支援它的浏覽器中,我們可以通過​

​+​

​操作符擷取Date對象的時間戳,也可以達到同樣的目的。

​// 取得開始時間​

​​

​var start = +new Date();​

​​

​​

​// 調用函數​

​​

​doSomething();​

​​

​​

​// 擷取結束時間​

​​

​var end = +new Date();​

​​

​​

​// 得到函數運作時間​

​​

​var​

繼承的方法

和其他引用類型一樣,Date 類型也重寫了 toLocaleString() 、toString() 和 valueOf() 方法,但這些方法的傳回值與其他類型中的方法不同。

  • Date 類型的 toLocalString() 方法會按照浏覽器設定的時區相适應的格式傳回日期和時間。這意味着時間格式中會包含AM和PM,但不會包含時區資訊,具體的格式會因浏覽器而異。​

​var now = new Date();​

​​

​​

​console.log(now.toLocaleString()); // 2018/3/20 上午10:33:32​

  • Date 類型的 toString() 方法會傳回帶有時區資訊的日期和時間,其中時間一般以軍用時間(範圍0到23)表示。​​

​var now = new Date();​

​​

​​

​console.log(now.toString()); // Tue Mar 20 2018 10:33:32 GMT+0800 (中國标準時間)​

  • Date 類型的 valueOf() 方法會傳回日期的毫秒表示,也就是時間戳。是以我們可以使用比較操作符來比較日期。

​var date1 = new Date(2018,1,1);​

​​

​var data2 = new Date(2018,3,18);​

​​

​​

​console.log(data1 < data2); // true​

在使用比較操作符,會隐式地調用 Date 對象的 valueOf() 方法,然後根據得到的毫秒數來進行比較。

其實在實際應用中,使用 toLocaleString() 和 toString() 來顯示日期時間是沒有什麼價值的,因為它們的傳回的日期格式在不同的浏覽器裡大相徑庭,無法得到一緻化的顯示結果,而且得到的格式對使用者的互動效果也不是很友好。

日期格式化方法

Date類型還有一些專門用來将日期轉化為字元串的方法,不過與 toLocaleString() 和 toString() 的缺點一樣,在平常的使用中沒有多大價值,是以僅做一下了解就好。

  • toDateString()——以特定于實作的格式顯示星期幾、月、日和年;
  • toTimeString()——以特定于實作的格式顯示時、分、秒和時區;
  • toLocaleDateString()——以特定于地區的格式顯示星期幾、月、日和年;
  • toLocaleTimeString()——以特定于實作的格式顯示時、分、秒;
  • toUTCString()——以特定于實作的格式完整的UTC日期。

日期/時間元件方法

上面我們已經提到了,Date類型本身的字元串格式化方法很雞肋,在日常使用中用處不大,是以一般我們隻有自己編寫适用于項目的 format 方法,這時我們一般需要用到擷取日期中特定部分的方法。方法有點多,如下:

  • getTime()

    傳回表示日期的毫秒數;與valueOf()方法傳回的值相同

  • setTime(毫秒)

    以毫秒數設定日期,會改變整個日期

  • getFullYear()

    取得4位數的年份(如2007而非僅07)

  • getUTCFullYear()

    傳回UTC日期的4位數年份

  • setFullYear(年)

    設定日期的年份。傳入的年份值必須是4位數字(如2007而非僅07)

  • setUTCFullYear(年)

    設定UTC日期的年份。傳入的年份值必須是4位數字(如2007而非僅07)

  • getMonth()

    傳回日期中的月份,其中0表示一月,11表示十二月

  • getUTCMonth()

    傳回UTC日期中的月份,其中0表示一月,11表示十二月

  • setMonth(月)

    設定日期的月份。傳入的月份值必須大于0,超過11則增加年份

  • setUTCMonth(月)

    設定UTC日期的月份。傳入的月份值必須大于0,超過11則增加年份

  • getDate()

    傳回日期月份中的天數(1到31)

  • getUTCDate()

    傳回UTC日期月份中的天數(1到31)

  • setDate(日)

    設定日期月份中的天數。如果傳入的值超過了該月中應有的天數,則增加月份

  • setUTCDate(日)

    設定UTC日期月份中的天數。如果傳入的值超過了該月中應有的天數,則增加月份

  • getDay()

    傳回日期中星期的星期幾(其中0表示星期日,6表示星期六)

  • getUTCDay()

    傳回UTC日期中星期的星期幾(其中0表示星期日,6表示星期六)

  • getHours()

    傳回日期中的小時數(0到23)

  • getUTCHours()

    傳回UTC日期中的小時數(0到23)

  • setHours(時)

    設定日期中的小時數。傳入的值超過了23則增加月份中的天數

  • setUTCHours(時)

    設定UTC日期中的小時數。傳入的值超過了23則增加月份中的天數

  • getMinutes()

    傳回日期中的分鐘數(0到59)

  • getUTCMinutes()

    傳回UTC日期中的分鐘數(0到59)

  • setMinutes(分)

    設定日期中的分鐘數。傳入的值超過59則增加小時數

  • setUTCMinutes(分)

    設定UTC日期中的分鐘數。傳入的值超過59則增加小時數

  • getSeconds()

    傳回日期中的秒數(0到59)

  • getUTCSeconds()

    傳回UTC日期中的秒數(0到59)

  • setSeconds(秒)

    設定日期中的秒數。傳入的值超過了59會增加分鐘數

  • setUTCSeconds(秒)

    設定UTC日期中的秒數。傳入的值超過了59會增加分鐘數

  • getMilliseconds()

    傳回日期中的毫秒數

  • getUTCMilliseconds()

    傳回UTC日期中的毫秒數

  • setMilliseconds(毫秒)

    設定日期中的毫秒數

  • setUTCMilliseconds(毫秒)

    設定UTC日期中的毫秒數

  • getTimezoneOffset()

    傳回本地時間與 UTC 時間相差的分鐘數。例如,美國東部标準時間傳回300。在某地進入夏令時的情況下,這個值會有所變化

時間戳的擷取

在平時我們需要用到最多的地方還是時間戳的擷取,上面我們也提到了不少可以的得到時間戳的方法,下面做一個歸納。

  • 使用 Date.now() 擷取目前時間的毫秒數,隻适用于目前時間。
  • 使用 Date.parse() 擷取指定時間的毫秒數,隻适用于指定時間。
  • 使用 Date.UTC() 擷取指定時間的毫秒數,隻适用于指定時間。
  • 使用操作符 ​

    ​+​

    ​ 擷取Date對象表示日期的毫秒數,都适用取決于Date對象。
  • 使用 valueOf() 擷取Date對象表示日期的毫秒數,都适用取決于Date對象。
  • 使用 getTime() 擷取Date對象表示日期的毫秒數,都适用取決于Date對象。

寫在最後

花了一些時間将 Date 類型的基礎知識,稍微總結了一下。這一塊還有很多内容,以後有時間還會對 Date 對象的格式化這個一塊進行補充。通過總結發現了很多自己以前習慣性忽略的一些東西。還是希望通過這樣的方式,把 JavaScript 這一塊的基礎打牢一點。不着急慢慢來 :)