Javascript 日期和時間

2023-02-17 10:49 更新

讓我們來學(xué)習(xí)一個新的內(nèi)建對象:日期(Date)。該對象存儲日期和時間,并提供了日期/時間的管理方法。

我們可以使用它來存儲創(chuàng)建/修改時間,測量時間,或者僅用來打印當(dāng)前時間。

創(chuàng)建

調(diào)用 new Date() 來創(chuàng)建一個新的 Date 對象。在調(diào)用時可以帶有一些參數(shù),如下所示:

?new Date()
?

不帶參數(shù) —— 創(chuàng)建一個表示當(dāng)前日期和時間的 Date 對象:

let now = new Date();
alert( now ); // 顯示當(dāng)前的日期/時間

?new Date(milliseconds)
?

創(chuàng)建一個 Date 對象,其時間等于 1970 年 1 月 1 日 UTC+0 之后經(jīng)過的毫秒數(shù)(1/1000 秒)。

// 0 表示 01.01.1970 UTC+0
let Jan01_1970 = new Date(0);
alert( Jan01_1970 );

// 現(xiàn)在增加 24 小時,得到 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ù)字表示形式。我們通常使用 new Date(timestamp) 通過時間戳來創(chuàng)建日期,并可以使用 date.getTime() 將現(xiàn)有的 Date 對象轉(zhuǎn)化為時間戳(下文會講到)。

在 01.01.1970 之前的日期帶有負(fù)的時間戳,例如:

// 31 Dec 1969
let Dec31_1969 = new Date(-24 * 3600 * 1000);
alert( Dec31_1969 );

?new Date(datestring)
?

如果只有一個參數(shù),并且是字符串,那么它會被自動解析。該算法與 Date.parse 所使用的算法相同,將在下文中進行介紹。

let date = new Date("2017-01-26");
alert(date);
// 未指定具體時間,所以假定時間為格林尼治標(biāo)準(zhǔn)時間(GMT)的午夜零點
// 并根據(jù)運行代碼時的用戶的時區(qū)進行調(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)前時區(qū)中的給定組件創(chuàng)建日期。只有前兩個參數(shù)是必須的。

  • ?year? 應(yīng)該是四位數(shù)。為了兼容性,也接受 2 位數(shù),并將其視為 ?19xx?,例如 ?98? 與 ?1998? 相同,但強烈建議始終使用 4 位數(shù)。
  • ?month? 計數(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); // 同樣,時分秒等均為默認(rèn)值 0

時間度量最大精確到 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 位數(shù))

getMonth()

獲取月份,從 0 到 11。

getDate()

獲取當(dāng)月的具體日期,從 1 到 31,這個方法名稱可能看起來有些令人疑惑。

getHours(),getMinutes(), getSeconds(),getMilliseconds()

獲取相應(yīng)的時間組件。

不是 ?getYear()?,而是 ?getFullYear()?

很多 JavaScript 引擎都實現(xiàn)了一個非標(biāo)準(zhǔn)化的方法 getYear()。不推薦使用這個方法。它有時候可能會返回 2 位的年份信息。永遠(yuǎn)不要使用它。要獲取年份就使用 getFullYear()。

另外,我們還可以獲取一周中的第幾天:

getDay()

獲取一周中的第幾天,從 0(星期日)到 6(星期六)。第一天始終是星期日,在某些國家可能不是這樣的習(xí)慣,但是這不能被改變。

以上的所有方法返回的組件都是基于當(dāng)?shù)貢r區(qū)的。

當(dāng)然,也有與當(dāng)?shù)貢r區(qū)的 UTC 對應(yīng)項,它們會返回基于 UTC+0 時區(qū)的日、月、年等:getUTCFullYear(),getUTCMonth(),getUTCDay()。只需要在 "get" 之后插入 "UTC" 即可。

如果你當(dāng)?shù)貢r區(qū)相對于 UTC 有偏移,那么下面代碼會顯示不同的小時數(shù):

//  當(dāng)前日期
let date = new Date();

// 當(dāng)?shù)貢r區(qū)的小時數(shù)
alert( date.getHours() );

// 在 UTC+0 時區(qū)的小時數(shù)(非夏令時的倫敦時間)
alert( date.getUTCHours() );

除了上述給定的方法,還有兩個沒有 UTC 變體的特殊方法:

getTime()

返回日期的時間戳 —— 從 1970-1-1 00:00:00 UTC+0 開始到現(xiàn)在所經(jīng)過的毫秒數(shù)。

getTimezoneOffset()

返回 UTC 與本地時區(qū)之間的時差,以分鐘為單位:

// 如果你在時區(qū) UTC-1,輸出 60
// 如果你在時區(qū) UTC+3,輸出 -180
alert( new Date().getTimezoneOffset() );

設(shè)置日期組件

下列方法可以設(shè)置日期/時間組件:

以上方法除了 setTime() 都有 UTC 變體,例如:setUTCHours()。

我們可以看到,有些方法可以一次性設(shè)置多個組件,比如 ?setHours?。未提及的組件不會被修改。

舉個例子:

let today = new Date();

today.setHours(0);
alert(today); // 日期依然是今天,但是小時數(shù)被改為了 0

today.setHours(0, 0, 0, 0);
alert(today); // 日期依然是今天,時間為 00:00:00。

自動校準(zhǔn)(Autocorrection)

自動校準(zhǔn) 是 Date 對象的一個非常方便的特性。我們可以設(shè)置超范圍的數(shù)值,它會自動校準(zhǔn)。

舉個例子:

let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!?
alert(date); // ……是 1st Feb 2013!

超出范圍的日期組件將會被自動分配。

假設(shè)我們要在日期 “28 Feb 2016” 上加 2 天。結(jié)果可能是 “2 Mar” 或 “1 Mar”,因為存在閏年。但是我們不需要考慮這些,只需要直接加 2 天,剩下的 Date 對象會幫我們處理:

let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);

alert( date ); // 1 Mar 2016

這個特性經(jīng)常被用來獲取給定時間段后的日期。例如,我們想獲取“現(xiàn)在 70 秒后”的日期:

let date = new Date();
date.setSeconds(date.getSeconds() + 70);

alert( date ); // 顯示正確的日期信息

我們還可以設(shè)置 0 甚至可以設(shè)置負(fù)值。例如:

let date = new Date(2016, 0, 2); // 2016 年 1 月 2 日

date.setDate(1); // 設(shè)置為當(dāng)月的第一天
alert( date );

date.setDate(0); // 天數(shù)最小可以設(shè)置為 1,所以這里設(shè)置的是上一月的最后一天
alert( date ); // 31 Dec 2015

日期轉(zhuǎn)化為數(shù)字,日期差值

當(dāng) Date 對象被轉(zhuǎn)化為數(shù)字時,得到的是對應(yīng)的時間戳,與使用 date.getTime() 的結(jié)果相同:

let date = new Date();
alert(+date); // 以毫秒為單位的數(shù)值,與使用 date.getTime() 的結(jié)果相同

有一個重要的副作用:日期可以相減,相減的結(jié)果是以毫秒為單位時間差。

這個作用可以用于時間測量:

let start = new Date(); // 開始測量時間

// do the job
for (let i = 0; i < 100000; i++) {
  let doSomething = i * i * i;
}

let end = new Date(); // 結(jié)束測量時間

alert( `The loop took ${end - start} ms` );

Date.now()

如果我們僅僅想要測量時間間隔,我們不需要 Date 對象。

有一個特殊的方法 Date.now(),它會返回當(dāng)前的時間戳。

它相當(dāng)于 new Date().getTime(),但它不會創(chuàng)建中間的 Date 對象。因此它更快,而且不會對垃圾回收造成額外的壓力。

這種方法很多時候因為方便,又或是因性能方面的考慮而被采用,例如使用 JavaScript 編寫游戲或其他的特殊應(yīng)用場景。

因此這樣做可能會更好:

let start = Date.now(); // 從 1 Jan 1970 至今的時間戳

// do the job
for (let i = 0; i < 100000; i++) {
  let doSomething = i * i * i;
}

let end = Date.now(); // 完成

alert( `The loop took ${end - start} ms` ); // 相減的是時間戳,而不是日期

基準(zhǔn)測試(Benchmarking)

在對一個很耗 CPU 性能的函數(shù)進行可靠的基準(zhǔn)測試(Benchmarking)時,我們需要謹(jǐn)慎一點。

例如,我們想判斷以下兩個計算日期差值的函數(shù):哪個更快?

這種性能測量通常稱為“基準(zhǔn)測試(benchmark)”。

// 我們有 date1 和 date2,哪個函數(shù)會更快地返回兩者的時間差?
function diffSubtract(date1, date2) {
  return date2 - date1;
}

// or
function diffGetTime(date1, date2) {
  return date2.getTime() - date1.getTime();
}

這兩個函數(shù)做的事情完全相同,但是其中一個函數(shù)使用顯式的 date.getTime() 來獲取毫秒形式的日期,另一個則依賴于“日期 — 數(shù)字”的轉(zhuǎn)換。它們的結(jié)果是一樣的。

那么,哪個更快呢?

首先想到的方法可能是連續(xù)運行兩者很多次,并計算所消耗的時間之差。就這個例子而言,函數(shù)過于簡單,所以我們必須執(zhí)行至少 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() 這種方式快得多,這是因為它沒有進行類型轉(zhuǎn)換,對引擎優(yōu)化來說更加簡單。

我們得到了結(jié)論,但是這并不是一個很好的度量的例子。

想象一下當(dāng)運行 bench(diffSubtract) 的同時,CPU 還在并行處理其他事務(wù),并且這也會占用資源。然而,運行 bench(diffGetTime) 的時候,并行處理的事務(wù)完成了。

對于現(xiàn)代多進程操作系統(tǒng)來說,這是一個非常常見的場景。

比起第二個函數(shù),第一個函數(shù)所能使用的 CPU 資源更少。這可能導(dǎo)致錯誤的結(jié)論。

為了得到更加可靠的度量,整個度量測試包應(yīng)該重新運行多次。

例如,像下面的代碼這樣:

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;

// 交替運行 bench(diffSubtract) 和 bench(diffGetTime) 各 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 引擎的先進優(yōu)化策略只對執(zhí)行很多次的 “hot code” 有效(對于執(zhí)行很少次數(shù)的代碼沒有必要優(yōu)化)。因此,在上面的例子中,第一次執(zhí)行的優(yōu)化程度不高。我們可能需要增加一個預(yù)熱步驟:

// 在主循環(huán)中增加預(yù)熱環(huán)節(jié)
bench(diffSubtract);
bench(diffGetTime);

// 開始度量
for (let i = 0; i < 10; i++) {
  time1 += bench(diffSubtract);
  time2 += bench(diffGetTime);
}

進行微型基準(zhǔn)測試時要小心

現(xiàn)代的 JavaScript 引擎執(zhí)行了很多優(yōu)化。與正常編寫的代碼相比,它們可能會改變“人為編寫的專用于測試的代碼”的執(zhí)行流程,特別是在我們對很小的代碼片段進行基準(zhǔn)測試時,例如某個運算符或內(nèi)建函數(shù)的工作方式。因此,為了深入理解性能問題,請學(xué)習(xí) JavaScript 引擎的工作原理。在那之后,你或許再也不需要進行微型基準(zhǔn)測試了。

http://mrale.ph 提供了很多 V8 引擎相關(guān)的文章。

對字符串調(diào)用 Date.parse

Date.parse(str) 方法可以從一個字符串中讀取日期。

字符串的格式應(yīng)該為:YYYY-MM-DDTHH:mm:ss.sssZ,其中:

  • ?YYYY-MM-DD? —— 日期:年-月-日。
  • 字符 ?"T"? 是一個分隔符。
  • ?HH:mm:ss.sss? —— 時間:小時,分鐘,秒,毫秒。
  • 可選字符 ?'Z'? 為 ?+-hh:mm? 格式的時區(qū)。單個字符 ?Z? 代表 UTC+0 時區(qū)。

簡短形式也是可以的,比如 YYYY-MM-DD 或 YYYY-MM,甚至可以是 YYYY。

Date.parse(str) 調(diào)用會解析給定格式的字符串,并返回時間戳(自 1970-01-01 00:00:00 起所經(jīng)過的毫秒數(shù))。如果給定字符串的格式不正確,則返回 NaN。

舉個例子:

let ms = Date.parse('2012-01-26T13:51:50.417-07:00');

alert(ms); // 1327611110417  (時間戳)

我們可以通過時間戳來立即創(chuàng)建一個 new Date 對象:

let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );

alert(date);

總結(jié)

  • 在 JavaScript 中,日期和時間使用 Date 對象來表示。我們不能單獨創(chuàng)建日期或時間,?Date? 對象總是同時創(chuàng)建兩者。
  • 月份從 0 開始計數(shù)(對,一月是 0)。
  • 一周中的某一天 ?getDay()? 同樣從 0 開始計算(0 代表星期日)。
  • 當(dāng)設(shè)置了超出范圍的組件時,?Date? 會進行自動校準(zhǔn)。這一點對于日/月/小時的加減很有用。
  • 日期可以相減,得到的是以毫秒表示的兩者的差值。因為當(dāng) ?Date? 被轉(zhuǎn)換為數(shù)字時,?Date? 對象會被轉(zhuǎn)換為時間戳。
  • 使用 ?Date.now()? 可以更快地獲取當(dāng)前時間的時間戳。

和其他系統(tǒng)不同,JavaScript 中時間戳以毫秒為單位,而不是秒。

有時我們需要更加精準(zhǔn)的時間度量。JavaScript 自身并沒有測量微秒的方法(百萬分之一秒),但大多數(shù)運行環(huán)境會提供。例如:瀏覽器有 performance.now() 方法來給出從頁面加載開始的以毫秒為單位的微秒數(shù)(精確到毫秒的小數(shù)點后三位):

alert(`Loading started ${performance.now()}ms ago`);
// 類似于 "Loading started 34731.26000000001ms ago"
// .26 表示的是微秒(260 微秒)
// 小數(shù)點后超過 3 位的數(shù)字是精度錯誤,只有前三位數(shù)字是正確的

Node.js 可以通過 microtime 模塊或使用其他方法。從技術(shù)上講,幾乎所有的設(shè)備和環(huán)境都允許獲取更高精度的時間數(shù)值,只不過不是使用 Date 對象。

任務(wù)


創(chuàng)建日期

重要程度: 5

創(chuàng)建一個 ?Date? 對象,日期是:Feb 20, 2012, 3:12am。時區(qū)是當(dāng)?shù)貢r區(qū)。

使用 ?alert? 顯示結(jié)果。


解決方案

new Date 構(gòu)造函數(shù)默認(rèn)使用本地時區(qū)。所以唯一需要牢記的就是月份從 0 開始計數(shù)。

所以二月對應(yīng)的數(shù)值是 1。

這是一個以數(shù)字作為日期參數(shù)的示例:

// new Date(year, month, date, hour, minute, second, millisecond)
let d1 = new Date(2012, 1, 20, 3, 12);
alert( d1 );

我們還可以從字符串創(chuàng)建日期,像這樣:

// new Date(datestring)
let d2 = new Date("2012-02-20T03:12");
alert( d2 );

顯示星期數(shù)

重要程度: 5

編寫一個函數(shù) ?getWeekDay(date)? 以短格式來顯示一個日期的星期數(shù):‘MO’,‘TU’,‘WE’,‘TH’,‘FR’,‘SA’,‘SU’。

例如:

let date = new Date(2012, 0, 3);  // 3 Jan 2012
alert( getWeekDay(date) );        // 應(yīng)該輸出 "TU"

解決方案

date.getDay() 方法返回從星期日開始的星期數(shù)。

我們創(chuàng)建一個關(guān)于星期的數(shù)組,這樣我們就可以通過編號獲取正確的日期名稱:

function getWeekDay(date) {
  let days = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA'];

  return days[date.getDay()];
}

let date = new Date(2014, 0, 3); // 3 Jan 2014
alert( getWeekDay(date) ); // FR

歐洲的星期表示方法

重要程度: 5

歐洲國家的星期計算是從星期一(數(shù)字 1)開始的,然后是星期二(數(shù)字 2),直到星期日(數(shù)字 7)。編寫一個函數(shù) getLocalDay(date),并返回日期的歐洲式星期數(shù)。

let date = new Date(2012, 0, 3);  // 3 Jan 2012
alert( getLocalDay(date) );       // 星期二,應(yīng)該顯示 2

解決方案

function getLocalDay(date) {

  let day = date.getDay();

  if (day == 0) { // weekday 0 (sunday) is 7 in european
    day = 7;
  }

  return day;
}

許多天之前是哪個月幾號?

重要程度: 4

寫一個函數(shù) getDateAgo(date, days),返回特定日期 date 往前 days 天是哪個月的哪一天。

例如,假設(shè)今天是 20 號,那么 getDateAgo(new Date(), 1) 的結(jié)果應(yīng)該是 19 號,getDateAgo(new Date(), 2) 的結(jié)果應(yīng)該是 18 號。

跨月、年也應(yīng)該是正確輸出:

let date = new Date(2015, 0, 2);

alert( getDateAgo(date, 1) ); // 1, (1 Jan 2015)
alert( getDateAgo(date, 2) ); // 31, (31 Dec 2014)
alert( getDateAgo(date, 365) ); // 2, (2 Jan 2014)

P.S. 函數(shù)不應(yīng)該修改給定的 date 值。


解決方案

思路很簡單:從 date 中減去給定的天數(shù):

function getDateAgo(date, days) {
  date.setDate(date.getDate() - days);
  return date.getDate();
}

……但是函數(shù)不能修改 date。這一點很重要,因為我們提供日期的外部代碼不希望它被修改。

要實現(xiàn)這一點,我們可以復(fù)制這個日期,就像這樣:

function getDateAgo(date, days) {
  let dateCopy = new Date(date);

  dateCopy.setDate(date.getDate() - days);
  return dateCopy.getDate();
}

let date = new Date(2015, 0, 2);

alert( getDateAgo(date, 1) ); // 1, (1 Jan 2015)
alert( getDateAgo(date, 2) ); // 31, (31 Dec 2014)
alert( getDateAgo(date, 365) ); // 2, (2 Jan 2014)

某月的最后一天?

重要程度: 5

寫一個函數(shù) getLastDayOfMonth(year, month) 返回 month 月的最后一天。有時候是 30,有時是 31,甚至在二月的時候會是 28/29。

參數(shù):

  • ?year? —— 四位數(shù)的年份,比如 2012。
  • ?month? —— 月份,從 0 到 11。

舉個例子,?getLastDayOfMonth(2012, 1) = 29?(閏年,二月)


解決方案

讓我們使用下個月創(chuàng)建日期,但將零作為天數(shù)(day)傳遞:

function getLastDayOfMonth(year, month) {
  let date = new Date(year, month + 1, 0);
  return date.getDate();
}

alert( getLastDayOfMonth(2012, 0) ); // 31
alert( getLastDayOfMonth(2012, 1) ); // 29
alert( getLastDayOfMonth(2013, 1) ); // 28

通常,日期從 1 開始,但從技術(shù)上講,我們可以傳遞任何數(shù)字,日期會自動進行調(diào)整。因此,當(dāng)我們傳遞 0 時,它的意思是“一個月的第一天的前一天”,換句話說:“上個月的最后一天”。


今天過去了多少秒?

重要程度: 5

寫一個函數(shù) getSecondsToday(),返回今天已經(jīng)過去了多少秒?

例如:如果現(xiàn)在是 10:00 am,并且沒有夏令時轉(zhuǎn)換,那么:

getSecondsToday() == 36000 // (3600 * 10)

該函數(shù)應(yīng)該在任意一天都能正確運行。那意味著,它不應(yīng)具有“今天”的硬編碼值。


解決方案

為獲取秒數(shù),我們可以使用今天的日期和 00:00:00 這個時間創(chuàng)建一個日期,然后使用當(dāng)前時間減去該時間。

不同之處在于,從今天之初開始算起的時間是以毫秒計算的,我們應(yīng)該將其除以 1000,進而得到秒數(shù):

function getSecondsToday() {
  let now = new Date();

  // 使用當(dāng)前的 day/month/year 創(chuàng)建一個對象
  let today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

  let diff = now - today; // ms difference
  return Math.round(diff / 1000); // make seconds
}

alert( getSecondsToday() );

另一種解決方法是獲取 hours/minutes/seconds,然后把它們轉(zhuǎn)換為秒數(shù):

function getSecondsToday() {
  let d = new Date();
  return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
};

alert( getSecondsToday() );

距離明天還有多少秒?

重要程度: 5

寫一個函數(shù) getSecondsToTomorrow(),返回距離明天的秒數(shù)。

例如,現(xiàn)在是 23:00,那么:

getSecondsToTomorrow() == 3600

P.S. 該函數(shù)應(yīng)該在任意一天都能正確運行。那意味著,它不應(yīng)具有“今天”的硬編碼值。


解決方案

為獲取距離明天的毫秒數(shù),我們可以用“明天 00:00:00”這個日期減去當(dāng)前的日期。

首先我們生成“明天”,然后對其進行減法操作:

function getSecondsToTomorrow() {
  let now = new Date();

  // tomorrow date
  let tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate()+1);

  let diff = tomorrow - now; // difference in ms
  return Math.round(diff / 1000); // convert to seconds
}

另一種解法:

function getSecondsToTomorrow() {
  let now = new Date();
  let hour = now.getHours();
  let minutes = now.getMinutes();
  let seconds = now.getSeconds();
  let totalSecondsToday = (hour * 60 + minutes) * 60 + seconds;
  let totalSecondsInADay = 86400;

  return totalSecondsInADay - totalSecondsToday;
}

請注意,很多國家有夏令時(DST),因此他們的一天可能有 23 小時或者 25 小時。我們對這些天數(shù)要區(qū)別對待。


格式化相對日期

重要程度: 4

寫一個函數(shù) formatDate(date),能夠?qū)?nbsp;date 進行如下格式化:

  • 如果 ?date? 距離現(xiàn)在不到 1 秒,輸出 ?"right now"?。
  • 否則,如果 ?date? 距離現(xiàn)在不到 1 分鐘,輸出 ?"n sec. ago"?。
  • 否則,如果不到 1 小時,輸出 ?"m min. ago"?。
  • 否則,以 ?"DD.MM.YY HH:mm"? 格式輸出完整日期。即:?"day.month.year hours:minutes"?,全部以兩位數(shù)格式表示,例如:?31.12.16 10:00?。

舉個例子:

alert( formatDate(new Date(new Date - 1)) ); // "right now"

alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago"

alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"

// 昨天的日期,例如 31.12.16 20:00
alert( formatDate(new Date(new Date - 86400 * 1000)) );

解決方案

為了獲取 date 距離當(dāng)前時間的間隔 —— 我們將兩個日期相減。

function formatDate(date) {
  let diff = new Date() - date; // 以毫秒表示的差值

  if (diff < 1000) { // 少于 1 秒
    return 'right now';
  }

  let sec = Math.floor(diff / 1000); // 將 diff 轉(zhuǎn)換為秒

  if (sec < 60) {
    return sec + ' sec. ago';
  }

  let min = Math.floor(diff / 60000); // 將 diff 轉(zhuǎn)換為分鐘
  if (min < 60) {
    return min + ' min. ago';
  }

  // 格式化 date
  // 將前置 0 加到一位數(shù) day/month/hours/minutes 前
  let d = date;
  d = [
    '0' + d.getDate(),
    '0' + (d.getMonth() + 1),
    '' + d.getFullYear(),
    '0' + d.getHours(),
    '0' + d.getMinutes()
  ].map(component => component.slice(-2)); // 得到每個組件的后兩位

  // 將時間信息和日期組合在一起
 return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':');
}

alert( formatDate(new Date(new Date - 1)) ); // "right now"

alert( formatDate(new Date(new Date - 30 * 1000)) ); // "30 sec. ago"

alert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // "5 min. ago"

// 昨天的日期如:31.12.2016 20:00
alert( formatDate(new Date(new Date - 86400 * 1000)) );

另一種解法:

function formatDate(date) {
  let dayOfMonth = date.getDate();
  let month = date.getMonth() + 1;
  let year = date.getFullYear();
  let hour = date.getHours();
  let minutes = date.getMinutes();
  let diffMs = new Date() - date;
  let diffSec = Math.round(diffMs / 1000);
  let diffMin = diffSec / 60;
  let diffHour = diffMin / 60;

  // 格式化
  year = year.toString().slice(-2);
  month = month < 10 ? '0' + month : month;
  dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth;
  hour = hour < 10 ? '0' + hour : hour;
  minutes = minutes < 10 ? '0' + minutes : minutes;

  if (diffSec < 1) {
    return 'right now';
  } else if (diffMin < 1) {
    return `${diffSec} sec. ago`
  } else if (diffHour < 1) {
    return `${diffMin} min. ago`
  } else {
    return `${dayOfMonth}.${month}.${year} ${hour}:${minutes}`
  }
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號