從 UTC 建立日期

預設情況下,Date 物件建立為本地時間。這並不總是令人滿意的,例如在伺服器和客戶端之間傳送不在同一時區的日期時。在這種情況下,根本不需要擔心時區,直到日期需要在當地時間顯示,如果甚至根本不需要的話。

問題

在這個問題中,我們希望與不同時區的某個人通知特定日期(日,月,年)。第一個實現天真地使用本地時間,這導致錯誤的結果。第二種實現使用 UTC 日期來避免不需要它們的時區。

使用錯誤結果的樸素方法

function formatDate(dayOfWeek, day, month, year) {
  var daysOfWeek = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
  var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  return daysOfWeek[dayOfWeek] + " " + months[month] + " " + day + " " + year;
}

//Foo lives in a country with timezone GMT + 1
var birthday = new Date(2000,0,1);
console.log("Foo was born on: " + formatDate(`birthday.getDay()`, `birthday.getDate()`,
      `birthday.getMonth()`, `birthday.getFullYear()`));

sendToBar(`birthday.getTime()`);

樣本輸出:Foo was born on: Sat Jan 1 2000

//Meanwhile somewhere else...

//Bar lives in a country with timezone GMT - 1
var birthday = new Date(`receiveFromFoo()`);
console.log("Foo was born on: " + formatDate(`birthday.getDay()`, `birthday.getDate()`,
      `birthday.getMonth()`, `birthday.getFullYear()`));

樣品輸出:Foo was born on: Fri Dec 31 1999

因此,Bar 總是相信 Foo 出生於 1999 年的最後一天。

正確的做法

function formatDate(dayOfWeek, day, month, year) {
  var daysOfWeek = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"];
  var months = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  return daysOfWeek[dayOfWeek] + " " + months[month] + " " + day + " " + year;
}

//Foo lives in a country with timezone GMT + 1
var birthday = new Date(Date.UTC(2000,0,1));
console.log("Foo was born on: " + formatDate(`birthday.getUTCDay()`, `birthday.getUTCDate()`,
      `birthday.getUTCMonth()`, `birthday.getUTCFullYear()`));

sendToBar(`birthday.getTime()`);

樣品輸出:Foo was born on: Sat Jan 1 2000

//Meanwhile somewhere else...

//Bar lives in a country with timezone GMT - 1
var birthday = new Date(`receiveFromFoo()`);
console.log("Foo was born on: " + formatDate(`birthday.getUTCDay()`, `birthday.getUTCDate()`, 
      `birthday.getUTCMonth()`, `birthday.getUTCFullYear()`));

樣品輸出:Foo was born on: Sat Jan 1 2000

從 UTC 建立日期

如果想要基於 UTC 或 GMT 建立 Date 物件,可以使用 Date.UTC(...) 方法。它使用與最長的 Date 建構函式相同的引數。此方法將返回一個數字,表示自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的時間。

console.log(Date.UTC(2000,0,31,12));

樣本輸出:949320000000

var utcDate = new Date(Date.UTC(2000,0,31,12));
console.log(utcDate);

樣品輸出:Mon Jan 31 2000 13:00:00 GMT+0100 (West-Europa (standaardtijd))

不出所料,UTC 時間和本地時間之間的差異實際上是時區偏移轉換為毫秒。

var utcDate = new Date(Date.UTC(2000,0,31,12));
var localDate = new Date(2000,0,31,12);

console.log(localDate - utcDate === `utcDate.getTimezoneOffset()` * 60 * 1000);

樣品輸出:true

更改 Date 物件

所有 Date 物件修飾符,例如 setDate(...)setFullYear(...) 都具有等效的 UTC 時間而不是本地時間的引數。

var date = new Date();
date.setUTCFullYear(2000,0,31);
date.setUTCHours(12,0,0,0);
console.log(date);

樣品輸出:Mon Jan 31 2000 13:00:00 GMT+0100 (West-Europa (standaardtijd))

其他 UTC 特定的修飾符是 .setUTCMonth().setUTCDate()(當月的某天),.setUTCMinutes().setUTCSeconds().setUTCMilliseconds()

使用 getTime() 和 setTime()避免歧義

如果需要上述方法來區分日期中的歧義,通常更容易將日期作為自 1970 年 1 月 1 日 00:00:00 UTC 以來經過的時間進行通訊。此單個數字表示單個時間點,並且可以在必要時轉換為本地時間。

var date = new Date(Date.UTC(2000,0,31,12));
var timestamp = date.getTime();
//Alternatively
var timestamp2 = Date.UTC(2000,0,31,12);
console.log(timestamp === timestamp2);

樣品輸出:true

//And when constructing a date from it elsewhere...
var otherDate = new Date(timestamp);

//Represented as an universal date
console.log(`otherDate.toUTCString()`);
//Represented as a local date
console.log(otherDate);

樣本輸出:

Mon, 31 Jan 2000 12:00:00 GMT
Mon Jan 31 2000 13:00:00 GMT+0100 (West-Europa (standaardtijd))