JavaScriptのnew Date()は書き方で9時間加算されるので注意

new Date()は書き方で9時間加算される

JavaScriptのnew Date()を使用すると現在日時を取得できます。

JavaScript
const date = new Date()
console.log(date)
// Sat Sep 28 2024 12:00:00 GMT+0900 (日本標準時)
console.log(date.getHours()) // 12

さらにnew Date()の引数に日時を入れると、その日時で取得できます。

JavaScript
const date = new Date('2024-09-28 12:00:00')
console.log(date)
// Sat Sep 28 2024 12:00:00 GMT+0900 (日本標準時)
console.log(date.getHours()) // 12

「そんなの当たり前だろう」と思う方が大半だと思いますが、日時の時間を省略した場合、引数の形式によって時刻が「00:00:00」または「09:00:00」になることまでは知らない人が多いです。

例えば 「new Date('2024/09/28')」と「new Date('2024-09-28')」とでは設定される時間が異なります。

JavaScript
const date1 = new Date('2024/09/28')
console.log(date1)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date1.getHours()) // 0

const date2 = new Date('2024-09-28')
console.log(date2)
// Sat Sep 28 2024 09:00:00 GMT+0900 (日本標準時)
console.log(date2.getHours()) // 9

これは引数の形式によって返す結果が「ローカルタイムゾーン」と「協定世界時 (UTC)」のどちらかになるからです。

協定世界時 (UTC)はイギリスのロンドン近郊のグリニッジのグリニッジ子午線の時刻と同じです。

ちなみにイギリス🇬🇧にはサマータイム(UTC+1)があるので、協定世界時 (UTC)とイギリスの時刻はサマータイム中は同じではありません。

協定世界時 (UTC)と日本🇯🇵の日時の時差は9時間なのでローカルタイムゾーンではなく、協定世界時 (UTC)を使用すると9時間加算されるというわけです。

これを知らないと意図せず日時に9時間加算してしまうケースがあるので注意が必要です。

このような問題に直面しないためにも、どのような引数だとローカルタイムゾーンになるのか知っておく必要があります。

ローカルタイムゾーンになるnew Date()の引数

ローカルタイムゾーンになるnew Date()の引数は大きくわけて3パターンあります。

※ 以下のパターンでは秒を省略してます。他にも引数の形式のパターンがありますが、レアケースなので割愛。

1. 時刻を指定する

日付だけでなく時刻を指定すれば、スラッシュ、ハイフン、カンマのいずれの形式でもローカルタイムゾーンになります。

JavaScript
const date1 = new Date('2024/09/28 00:00')
console.log(date1)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date1.getHours()) // 0

const date2 = new Date('2024-09-28 00:00')
console.log(date2)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date2.getHours()) // 0

const date3 = new Date(2024, 9, 28, 0, 0)
console.log(date3)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date3.getHours()) // 0

ハイフンだけは時刻を省略すると協定世界時 (UTC)になってしまいます。

JavaScript
const date1 = new Date('2024/09/28')
console.log(date1)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date1.getHours()) // 0

const date2 = new Date('2024-09-28')
console.log(date2)
// Sat Sep 28 2024 09:00:00 GMT+0900 (日本標準時)
console.log(date2.getHours()) // 9

const date3 = new Date(2024, 9, 28)
console.log(date3)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date3.getHours()) // 0

2. タイムスタンプを指定する

日付を数値(タイムスタンプ)として渡すとローカルタイムゾーンになります。

タイムスタンプだと数値を見ただけでは日時がわからなくなるのが欠点です。

JavaScript
const date = new Date(1727449200000)
console.log(date)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(date.getHours()) // 0

3. ISO 8601形式(Zなし)で指定する

new Date('2024-09-28T00:00Z') の形式を「ISO 8601形式」といいます。

ISO 8601形式には以下のような特徴があります。

  1. 年-月-日 の順で日付をハイフンで区切る
  2. 日付と時刻を「T」で区切る
  3. 時間は24時間制で「:」で区切る
  4. Zは協定世界時 (UTC)を示しており、Zなしはローカルタイムゾーンになる

ISO 8601形式のZの有無によって協定世界時 (UTC)またはローカルタイムゾーンになることは知らない人が多く、はまりやすいポイントなので注意が必要です。

JavaScript
const dateZ = new Date('2024-09-28T00:00Z')
console.log(dateZ)
// Sat Sep 28 2024 09:00:00 GMT+0900 (日本標準時)
console.log(dateZ.getHours()) // 9

const dateNoZ = new Date('2024-09-28T00:00')
console.log(dateNoZ)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)
console.log(dateNoZ.getHours()) // 0

JSON.stringifyで変換すると協定世界時 (UTC)になる

new Date()の引数にISO 8601形式(Zなし)で指定しても、JSON.stringifyで文字列に変換すると協定世界時 (UTC)になってしまいます。

JavaScript
const data = {
  id: 1,
  loginDate: new Date('2024-09-28T00:00')
}

const str = JSON.stringify(data)
console.log(str)
// {"id":1,"loginDate":"2024-09-27T15:00:00.000Z"}

JSON.stringifyでローカルタイムゾーンが協定世界時 (UTC)になるのを防ぐには、Dateの場合は事前に文字列に変換して、JSON.parseの際はnew Date()で再変換する必要があります。

JavaScript
const data = {
  id: 1,
  loginDate: new Date('2024-09-28T00:00')
}

function convertDateStringify(obj) {
  Object.keys(obj).forEach((key) => {
    if (obj[key] instanceof Date) {
      obj[key] = 'Date:::' + obj[key]
    }
  });
  return JSON.stringify(obj)
}

function convertDateParse(str) {
  const parseData = JSON.parse(strData)
  const regex = /^Date:::/
  Object.keys(parseData).forEach((key) => {
    if (regex.test(parseData[key])) {
      parseData[key] = new Date(parseData[key].replace(regex, ''))
    }
  })
  return parseData
}

const strData = convertDateStringify(data)
console.log(strData)
// {"id":1,"loginDate":"Date:::Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)"}

const result = convertDateParse(strData)
console.log(result.loginDate instanceof Date) // true
console.log(result.loginDate)
// Sat Sep 28 2024 00:00:00 GMT+0900 (日本標準時)

JSON.stringifyでローカルタイムゾーンが協定世界時 (UTC)になることも認知度が低いので、JSON.stringifyを使用している場合はオブジェクトにDateが含まれていないか注意が必要です。