JavaScriptには、日時を保持し、その管理のためのメソッドを提供する組み込みオブジェクトDateがあります。
DateはJavaのjava.util.Dateクラスを基にしています。このクラスは2010年代初頭に置き換えられましたが、後方互換性のために、JavaScriptのDateは今も残っています。
一般にDateの扱いは難しく、タイムゾーンの処理も不十分か、まったく存在しないため、moment.js、day.js、date-fns、luxonなど、多くのライブラリが存在します。
これらのライブラリは、Exercismでは利用できません。
自分のプロジェクトでは、moment.jsのような非推奨・メンテナンスされていないパッケージは使わず、luxonのようなよりモダンな代替手段や、まだ広く利用できないTemporalに頼りましょう。
この演習ではDateに焦点を当てます。DateはJavaScriptが続く限り、ずっと重要であり続けます。
Dateオブジェクトは、Dateクラスのインスタンスです。
コンストラクター関数に引数を渡さずに作成することもできます。
こうすると、現在の日時を表すDateオブジェクトが得られます。
const now = new Date();
// => Thu Apr 14 2022 11:46:08 GMT+0530 (India Standard Time)
// Shows current day, date and time (in your time zone).
数値を渡すと、それはtimestampとして解釈されます。
タイムスタンプは、1970年1月1日 UTC+0から経過したミリ秒数を表す整数です。
const epoch = new Date(0);
// Thu Jan 01 1970 01:00:00 GMT+0100 (Central European Standard Time)
const another = new Date(1749508766627);
// Tue Jun 10 2025 00:39:26 GMT+0200 (Central European Summer Time)
new Date(0)が「最も早い」日付オブジェクトを生成すると期待するかもしれません。しかしJavaScriptは日付をローカルのタイムゾーンに変換するため、実際にUnixエポックの値を得るのは、GMT / UTC+0のタイムゾーン付近にいる人だけです。
日付を表す文字列の値を、Dateコンストラクターに渡すことができます。
実装間で一貫している唯一の形式は、国際的に認識され標準化された、いわゆるISO 8601タイムスタンプ文字列の簡略版です。
UTCでのある時点は、次のように表されます。
YYYY-MM-DDTHH:mm:ss.mssZ
YYYYMMDDTHHmmss.mssZ
各部分は次のように置き換わります。
| キー | 説明 | デフォルト |
|---|---|---|
| YYYY | 暦年、4桁で表記 | |
| MM | 暦月、2桁で表記 | 01 |
| DD | 暦日、2桁で表記 | 01 |
| T | 文字そのままのT。日付と時刻を区切ります | |
| HH | 24時間制の時、2桁 | 00 |
| mm | 分、2桁 | 00 |
| ss | 秒、2桁 | 00 |
| mss | ミリ秒、3桁 | 000 |
| Z | 文字そのままのZ、またはオフセット+/-HH:mm
|
文字そのままのZはUTCを表します(タイムゾーンなし、夏時間なし)。
ほとんどの要素にはデフォルト値があるので、末尾の部分を省略してもかまいません。
YYYY-MM-DD
時刻はデフォルトで00:00:00.000になります。
タイムスタンプがZで終わらず、タイムゾーンのオフセットを示す+HH:mmや-HH:mmでも終わらない場合、歴史的な理由から、次のことが当てはまります。
タイムゾーンのオフセットがない場合、日付のみの形式はUTC時刻として解釈され、日付と時刻の形式はローカル時刻として解釈されます。 UTC時刻としての解釈は、ISO 8601と整合しない歴史的な仕様の誤りによるものですが、ウェブの互換性のために変更できませんでした。 詳しくはBroken Parser – A Web Reality Issueを参照してください。
Date.parseが受け付けるその他の形式は、動くことも動かないこともあります。
JavaScriptでDateを扱うときは、stringからDateに変換する際には_必ず_ISO 8601タイムスタンプを使いましょう。
日付のみの形式は許可されていますが、すべてのISO 8601形式がサポートされているわけではありません。 MDNの簡略版の説明ページを参照してください。
既存のDateオブジェクトをコンストラクターの引数として使うこともできます。
こうすると、同じ日時を持つ既存のDateオブジェクトのコピーが作られます。
const t1 = new Date();
const t2 = new Date(t1);
// Values of t1 and t2 will be the same.
日付だけを表すDateは、数値を3つ渡して作成できます。 日付と時刻を表すDateは、数値を6つ渡して作成できます。
const date1 = new Date(95, 11, 17);
// Creates Date for Dec 17 1995 00:00 if your local timezone is equivalent to UTC.
const date2 = new Date(2013, 12, 5, 13, 24, 0);
// Creates Date for Jan 5 2014 13:24 if your local timezone is equivalent to UTC.
2番目の値はmonthで、1月が0から始まり、12月が11になります。
Date.parse()日付をパースする関数Date.parseへの言及や参照を見かけることがあるかもしれません。
この関数には不変の性質(常に成り立つこと)がわずかしかなく、残りの実装は仕様で定められていない(したがって標準ではない)ため、使うべきではありません。
Dateの要素にアクセスするDateオブジェクトには、日付の要素を返すさまざまなメソッドがあります。
getFullYear(); // Get the year (4 digits)
getMonth(); // Get the month, from 0 to 11.
getDate(); // Get the day of month, from 1 to 31.
getHours(); // Get the hour in a 24 clock, from 0 to 23
getMinutes(); // Get the minutes, from 0 to 59
getSeconds(); // Get the seconds, from 0 to 59
getMilliseconds(); // Get the milliseconds, from 0 and 999
getDay(); // Get the day of week, from 0 (Sunday) to 6 (Saturday).
これらのそれぞれには、値を更新するための対応するset版(例:setFullYear)があります。
あふれた値は、次の要素に繰り越されます。
const date = new Date('2025-02-28T12:42:00Z');
// => Fri Feb 28 2025 13:42:00 GMT+0100 (Central European Standard Time)
date.setDate(29);
// there was no February 29th in 2025.
date.getDate();
// => 1
date.toString();
// => Sat Mar 01 2025 13:42:00 GMT+0100 (Central European Standard Time)
すべてのメソッドには、ローカルのタイムゾーンを無視するUTC版があります。
Dateオブジェクトには、UNIXタイムスタンプをミリ秒で返すメソッドgetTime()があります。つまり、1970年1月1日UTCの午前0時からのミリ秒数です。
さらに、DateオブジェクトをISO 8601タイムスタンプ文字列に変換するメソッドtoISOString()も利用できます。
より大きい(>)と以上(>=)、およびより小さい(<)と以下(<=)は、2つのDateの間、またはDateと数値の間で直接使えます。
これは、JavaScriptがDateをプリミティブに変換しようとするために機能します。
2つのDateの間、またはDateと数値の間で比較を行うとき、JavaScriptは[Symbol.toPrimitive]("number")を呼び出し、その内部でdate.valueOf()を呼び出します。
後者はdate.getTime()を呼び出すのと同じです。
この動作に頼りたくない場合は、先にgetTime()を使って数値に変換しましょう。
Dateは等価(==や===)では比較できませんが、.getTime()の結果なら比較できます。
この演習では、予約を管理するためのいくつかの関数に取り組みます。 システムはすべてをISO 8601形式の文字列で保存しますが、人がカレンダーを使うときの感覚とは違います。 さまざまな形式の間で変換するには、複数の関数が必要です。
現在時刻からn日後に予約を作成します。
この関数はn日を受け取り、n日後の予約時刻を返します。
createAppointment(4, now);
// Given now is Sun Oct 05 2022 23:28:43 GMT+0600 (Bangladesh Standard Time)
// => Sun Oct 09 2022 23:28:43 GMT+0600 (Bangladesh Standard Time)
2番目の仮引数nowを使わない場合は、代わりに現在時刻を使います。
さまざまなツールは、国際標準のISO 8601形式でしか扱えません。
日付を受け取って、その形式の文字列を返す関数getAppointmentTimestampを書きます。
const appointment = new Date(Date.UTC(2010, 6, 16, 12, 42, 0, 0));
getAppointmentTimestamp(appointment);
// => '2010-07-16T12:42:00.000Z'
タイムスタンプは読みにくいものです。予約の詳細を取得する関数があると、その助けになります。
関数getAppointmentDetailsはISO 8601形式のタイムスタンプを受け取り、年、月、日、時、分を返します。
getAppointmentDetails('2022-04-24T08:15:00.000');
// => { year: 2022, month: 3, date: 24, hour: 8, minute: 15 }
この関数は、第1引数に予約時刻、第2引数にいくつかのオプションを持つオブジェクトを受け取ります。 オブジェクト内のオプションに従って予約を更新し、新しい予約日時を返します。 オプションオブジェクトには複数のオプションを指定できます。
updateAppointment('2022-02-09T09:20:00.000', { month: 6 });
// => { year: 2022, month: 6, date: 9, hour: 10, minute: 20 }
この関数は、2つの予約(タイムスタンプ)を引数として受け取ります。 2つの時刻の差を秒で返します。
0.5秒はほとんど意味がないので、返す前に数値を丸めます。
timeBetween('2022-12-12T09:20:00.000', '2022-12-18T08:30:00.000');
// => 515400
最後に、予約が作成されると、システムはそれが有効かどうかを確認する必要があります。 つまり、予約は未来の時刻でなければならず、過去の時刻であってはなりません。
2つの引数、予約のタイムスタンプ(文字列)と現在時刻のタイムスタンプ(文字列)を受け取り、現在時刻を基準にして予約が未来であればtrueを返す関数isValidを書きます。
isValid('2022-02-11T23:00:00.000', '2022-02-08T23:00:00.000');
// => true
Exercismに登録すれば、37個のコンセプト159個の演習、そして本物の人間によるメンタリングとともに、JavaScriptを学んでマスターできます。すべて無料です。