Date

この章では、JavaScriptで日付や時刻を扱うためのDateについて学びます。

Dateオブジェクト

DateオブジェクトはStringArrayなどと同じく、ビルトインのグローバルオブジェクトです。 そのため、スクリプト中のどこからでも呼び出して使えます。

Dateオブジェクトをインスタンス化することで、ある特定の時刻を表すオブジェクトが得られます。 Dateにおける「時刻」は、UTC(協定世界時)の1970年1月1日0時0分0秒を基準とした相対的なミリ秒として保持されます。 このミリ秒の値のことを、本章では「時刻値」と呼びます。 Dateオブジェクトのインスタンスはそれぞれがひとつの時刻値をもち、その時刻値をもとに日付や時・分などを扱うメソッドを提供します。

インスタンスの作成

Dateオブジェクトのインスタンスは、常にnew演算子を使って作成します。 Dateオブジェクトのインスタンス作成には、大きく分けて2つの種類があります。 ひとつは現在の時刻をインスタンス化するもの、もうひとつは任意の時刻をインスタンス化するものです。

現在の時刻をインスタンス化する

Dateをnewするときにコンストラクタ引数を何も渡さない場合、作成されるインスタンスは現在の時刻を表すものになります。 Dateオブジェクトのインスタンスではなく現在の時刻の時刻値だけが欲しい場合には、Date.nowメソッドの戻り値を使います。 作成したインスタンスがもつ時刻値は、getTimeメソッドで取得できます。 また、toISOStringメソッドを使うと、その時刻をUTCにおけるISO 8601形式の文字列に変換できます。 ISO 8601とは国際規格となっている文字列の形式で、2006-01-02T15:04:05.999+09:00のように時刻を表現します。 人間が見ても分かりやすい文字列であるため、広く利用されています。

// 現在の時刻を表すインスタンスを作成する
const now = new Date();
// 時刻値だけが欲しい場合にはDate.nowメソッドを使う
console.log(Date.now());

// 時刻値を取得する
console.log(now.getTime());
// 時刻をISO 8601形式の文字列で表示する
console.log(now.toISOString());

任意の時刻をインスタンス化する

コンストラクタ引数を渡すことで、任意の時刻を表すインスタンスを作成できます。 Dateのコンストラクタ関数はオーバーロードされており、渡す引数によって時刻の指定方法が変わります。 オーバーロードは次の3種類があります。

  • 時刻値を渡すもの
  • 時刻を示す文字列を渡すもの
  • 時刻の部分(年・月・日など)をそれぞれ数値で渡すもの

1つめは、コンストラクタ関数にミリ秒を表す数値型の引数を渡したときに適用されます。 渡した数値をUTCの1970年1月1日0時0分0秒を基準とした時刻値として扱います。 この方法は実行環境による挙動の違いが起きないので安全です。 また、時刻値を直接指定するので、他2つの方法と違ってタイムゾーンを考慮する必要がありません。

// 時刻のミリ秒値を直接指定する形式
// 1136214245999はUTCにおける"2006年1月2日15時04分05秒999"を表す
const date = new Date(1136214245999);
// 末尾の'Z'はUTCであることを表す
console.log(date.toISOString()); // => "2006-01-02T15:04:05.999Z"

2つめは文字列型の引数を渡したときに適用されます。 RFC2822ISO 8601の形式にしたがった文字列を渡すと、 その文字列をパースして得られる時刻値を使って、Dateのインスタンスを作成します。

次のコードでは、ISO 8601形式の文字列を渡してDateのインスタンスを作成します。 タイムゾーンを含む文字列の場合は、そのタイムゾーンにおける時刻として時刻値を計算します。 文字列からタイムゾーンが読み取れない場合は、実行環境のタイムゾーンによって時刻値を計算するため注意が必要です。 また、ISO 8601形式以外の文字列のパースは、ブラウザごとに異なる結果を返す可能性があるため注意しましょう。

// UTCにおける"2006年1月2日15時04分05秒999"を表すISO 8601形式の文字列
const inUTC = new Date("2006-01-02T15:04:05.999Z");
console.log(inUTC.toISOString()); // => "2006-01-02T15:04:05.999Z"

// 上記の例とは異なり、UTCであることを表す'Z'がついていないことに注意
// Asia/Tokyo(+09:00)で実行すると、UTCにおける表記は9時間前の06時04分05秒になる
const inLocal = new Date("2006-01-02T15:04:05.999");
console.log(inLocal.toISOString()); // "2006-01-02T06:04:05.999Z" (Asia/Tokyoの場合)

3つめは、時刻を次のように、年・月・日などの部分ごとの数値で指定する方法です。

new Date(year, month, day, hour, minutes, seconds, milliseconds);

コンストラクタ関数に2つ以上の引数を渡すと、このオーバーロードが適用されます。 日を表す第3引数から後ろの引数は省略可能ですが、日付だけはデフォルトで1が設定され、その他は0が設定されます。 また、月を表す第2引数は0から11までの数値で指定することにも注意しましょう。

先述した2つの方法と違い、この方法はタイムゾーンを指定できません。 渡した数値は常にローカルのタイムゾーンにおける時刻とみなされます。 結果が実行環境に依存してしまうため、基本的にこの方法は使うべきでありません。 時刻を部分ごとに指定したい場合は、Date.UTCメソッドを使うとよいでしょう。 渡す引数の形式は同じですが、Date.UTCメソッドは渡された数値をUTCにおける時刻として扱い、その時刻値を返します。

// 実行環境における"2006年1月2日15時04分05秒999"を表す
// タイムゾーンを指定することはできない
const date1 = new Date(2006, 0, 2, 15, 4, 5, 999);
console.log(date1.toISOString()); // "2006-01-02T06:04:05.999Z" (Asia/Tokyoの場合)

// Date.UTCメソッドを使うとUTCに固定できる
const ms = Date.UTC(2006, 0, 2, 15, 4, 5, 999);
// 時刻値を渡すコンストラクタと併用する
const date2 = new Date(ms);
console.log(date2.toISOString()); // => "2006-01-02T15:04:05.999Z"

なお、どのオーバーロードにもあてはまらない引数や、時刻としてパースできない文字列を渡した際にも、Dateのインスタンスは作成されます。 ただし、このインスタンスがもつ時刻は不正であるため、getTimeメソッドはNaNを返し、toStringメソッドはInvalid Dateという文字列を返します。

// 不正なDateインスタンスを作成する
const invalid = new Date("");
console.log(invalid.getTime()); // => NaN
console.log(invalid.toString()); // => "Invalid Date"

Dateのインスタンスメソッド

Dateオブジェクトのインスタンスは多くのメソッドをもっていますが、 ほとんどはgetHourssetHoursのような、時刻の各部分を取得・更新するためのメソッドです。

次の例は、日付を決まった形式の文字列に変換しています。 getMonthメソッドやsetMonthメソッドのように月を数値で扱うメソッドは、0から11の数値で指定することに注意しましょう。あるDateのインスタンスの時刻が何月かを表示するには、getMonthメソッドの戻り値に1を足す必要があります。

// YYYY/MM/DD形式の文字列に変換する関数
function formatDate(date) {
    const yyyy = new String(date.getFullYear());
    // String#padStartメソッド(ES2017)で2桁になるように0埋めする
    const mm = new String(date.getMonth() + 1).padStart(2, "0");
    const dd = new String(date.getDate()).padStart(2, "0");
    return `${yyyy}/${mm}/${dd}`;
}

const date = new Date("2006-01-02T15:04:05.999");
console.log(formatDate(date)); // => "2006/01/02"

getTimezoneOffsetメソッドは、実行環境のタイムゾーンのUTCからのオフセット値を単位の数値で返します。 たとえばAsia/TokyoタイムゾーンはUTC+9時間なのでオフセット値は-9時間となり、getTimezoneOffsetメソッドの戻り値は-540です。

// getTimezoneOffsetはインスタンスメソッドなので、インスタンスが必要
const now = new Date();
// 時間単位にしたタイムゾーンオフセット
const timezoneOffsetInHours = now.getTimezoneOffset() / 60;
// UTCの現在の時間を計算できる
console.log(`Hours in UTC: ${now.getHours() + timezoneOffsetInHours}`);

現実のユースケースとDate

ここまでDateオブジェクトとインスタンスメソッドについて述べましたが、 多くのユースケースにおいては機能が不十分です。 たとえば次のような場合に、Dateでは直感的に記述できません。

  • 任意の書式の文字列から時刻に変換するメソッドがない
  • 「時刻を1時間進める」のように時刻を前後にずらす操作を提供するメソッドがない
  • 任意のタイムゾーンにおける時刻を計算するメソッドがない
  • YYYY/MM/DDのようなフォーマットに基づいた文字列への変換を提供するメソッドがない

そのため、JavaScriptにおける日付・時刻の処理は、標準のDateではなくライブラリを使うことが一般的になっています。 代表的なライブラリとしては、moment.jsjs-jodadate-fnsなどがあります。

// moment.jsで現在時刻のmomentオブジェクトを作る
const now = moment();
// addメソッドで10分進める
const future = now.add(10, "minutes");
// formatメソッドで任意の書式の文字列に変換する
console.log(future.format("YYYY/MM/DD"));