新しい組み込みオブジェクトであるDateについて学びましょう。これは日付と時刻を格納し、日付/時刻管理のためのメソッドを提供します。
たとえば、作成/変更時刻の保存、時間の測定、現在の日付の出力などに使用できます。
作成
新しいDate
オブジェクトを作成するには、次のいずれかの引数を指定してnew Date()
を呼び出します。
new Date()
-
引数なし - 現在の日付と時刻の
Date
オブジェクトを作成します。let now = new Date(); alert( now ); // shows current date/time
new Date(milliseconds)
-
1970年1月1日UTC+0から経過したミリ秒数(1秒の1/1000)に等しい時刻を持つ
Date
オブジェクトを作成します。// 0 means 01.01.1970 UTC+0 let Jan01_1970 = new Date(0); alert( Jan01_1970 ); // now add 24 hours, get 02.01.1970 UTC+0 let Jan02_1970 = new Date(24 * 3600 * 1000); alert( Jan02_1970 );
1970年の初めから経過したミリ秒数を表す整数を、タイムスタンプと呼びます。
これは、日付を軽量な数値で表現したものです。
new Date(timestamp)
を使用してタイムスタンプから日付を作成し、date.getTime()
メソッドを使用して既存のDate
オブジェクトをタイムスタンプに変換することができます(下記参照)。1970年1月1日より前の日付は、負のタイムスタンプを持ちます。例:
// 31 Dec 1969 let Dec31_1969 = new Date(-24 * 3600 * 1000); alert( Dec31_1969 );
new Date(datestring)
-
引数が1つで、それが文字列の場合、自動的に解析されます。アルゴリズムは
Date.parse
と同じです。後ほど説明します。let date = new Date("2017-01-26"); alert(date); // The time is not set, so it's assumed to be midnight GMT and // is adjusted according to the timezone the code is run in // So the result could be // Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time) // or // Wed Jan 25 2017 16:00:00 GMT-0800 (Pacific Standard Time)
new Date(year, month, date, hours, minutes, seconds, ms)
-
ローカルタイムゾーンで指定されたコンポーネントを使用して日付を作成します。最初の2つの引数のみが必須です。
year
は4桁である必要があります。互換性のために、2桁も受け入れられ、19xx
とみなされます。たとえば、ここでは98
は1998
と同じですが、常に4桁を使用することを強くお勧めします。month
カウントは0
(1月)から11
(12月)までです。date
パラメータは実際には日ですが、省略した場合は1
とみなされます。hours/minutes/seconds/ms
が省略されている場合、0
とみなされます。
例えば
new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00 new Date(2011, 0, 1); // the same, hours etc are 0 by default
最大の精度は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桁)。
- getMonth()
- 月を取得します。0から11までです。
- getDate()
- 1から31までの日を取得します。メソッドの名前は少し奇妙に見えます。
- getHours()、getMinutes()、getSeconds()、getMilliseconds()
- 対応する時間コンポーネントを取得します。
getYear()
ではなく、getFullYear()
を使用してください。多くのJavaScriptエンジンは、非標準のメソッドであるgetYear()
を実装しています。このメソッドは非推奨です。これは、2桁の年を返す場合があります。使用しないでください。年にはgetFullYear()
があります。
さらに、曜日を取得できます。
- getDay()
0
(日曜日)から6
(土曜日)までの曜日を取得します。最初の日曜日は常に日曜日です。国によってはそうではありませんが、変更することはできません。
上記のすべてのメソッドは、ローカルタイムゾーンに関連するコンポーネントを返します。
UTC+0タイムゾーンの日、月、年などを返すUTC対応のメソッドもあります:getUTCFullYear()、getUTCMonth()、getUTCDay()。「get」の直後に「UTC」を挿入するだけです。
ローカルタイムゾーンがUTCに対してずれている場合、以下のコードは異なる時間を表示します。
// current date
let date = new Date();
// the hour in your current time zone
alert( date.getHours() );
// the hour in UTC+0 time zone (London time without daylight savings)
alert( date.getUTCHours() );
上記のメソッドに加えて、UTCバリアントを持たない2つの特別なメソッドがあります。
- getTime()
-
日付のタイムスタンプ(1970年1月1日UTC+0から経過したミリ秒数)を返します。
- getTimezoneOffset()
-
UTCとローカルタイムゾーンの差を分単位で返します。
// if you are in timezone UTC-1, outputs 60 // if you are in timezone UTC+3, outputs -180 alert( new Date().getTimezoneOffset() );
日付コンポーネントの設定
次のメソッドを使用すると、日付/時刻コンポーネントを設定できます。
setFullYear(year, [month], [date])
setMonth(month, [date])
setDate(date)
setHours(hour, [min], [sec], [ms])
setMinutes(min, [sec], [ms])
setSeconds(sec, [ms])
setMilliseconds(ms)
setTime(milliseconds)
(1970年1月1日UTCからのミリ秒で日付全体を設定します)
setTime()
を除くすべてにUTCバリアントがあります。たとえば、setUTCHours()
です。
ご覧のとおり、一部のメソッドは一度に複数のコンポーネントを設定できます。たとえば、setHours
です。言及されていないコンポーネントは変更されません。
例えば
let today = new Date();
today.setHours(0);
alert(today); // still today, but the hour is changed to 0
today.setHours(0, 0, 0, 0);
alert(today); // still today, now 00:00:00 sharp.
自動修正
自動修正は、Date
オブジェクトの非常に便利な機能です。範囲外の値を設定でき、自動的に調整されます。
例えば
let date = new Date(2013, 0, 32); // 32 Jan 2013 ?!?
alert(date); // ...is 1st Feb 2013!
範囲外の日付コンポーネントは自動的に配分されます。
日付「2016年2月28日」を2日増やす必要があるとします。うるう年の場合は「3月2日」または「3月1日」になる場合があります。それについて考える必要はありません。2日を追加するだけです。 Date
オブジェクトが残りを処理します。
let date = new Date(2016, 1, 28);
date.setDate(date.getDate() + 2);
alert( date ); // 1 Mar 2016
この機能は、指定された期間後の日付を取得するためによく使用されます。たとえば、「今から70秒後」の日付を取得してみましょう。
let date = new Date();
date.setSeconds(date.getSeconds() + 70);
alert( date ); // shows the correct date
ゼロまたは負の値を設定することもできます。例えば
let date = new Date(2016, 0, 2); // 2 Jan 2016
date.setDate(1); // set day 1 of month
alert( date );
date.setDate(0); // min day is 1, so the last day of the previous month is assumed
alert( date ); // 31 Dec 2015
日付を数値に変換、日付の差
Date
オブジェクトが数値に変換されると、date.getTime()
と同じタイムスタンプになります。
let date = new Date();
alert(+date); // the number of milliseconds, same as date.getTime()
重要な副作用:日付を減算でき、結果はミリ秒単位の差になります。
これは、時間測定に使用できます。
let start = new Date(); // start measuring time
// do the job
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = new Date(); // end measuring time
alert( `The loop took ${end - start} ms` );
Date.now()
時間を測定したいだけなら、Date
オブジェクトは必要ありません。
現在のタイムスタンプを返す特別なメソッドDate.now()
があります。
これはセマンティック的にはnew Date().getTime()
と同じですが、中間Date
オブジェクトを作成しません。そのため、高速で、ガベージコレクションに負担をかけません。
これは、主に利便性のため、またはJavaScriptのゲームやその他の特殊なアプリケーションのようにパフォーマンスが重要な場合に使用されます。
そのため、おそらくこれが良いでしょう。
let start = Date.now(); // milliseconds count from 1 Jan 1970
// do the job
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = Date.now(); // done
alert( `The loop took ${end - start} ms` ); // subtract numbers, not dates
ベンチマーク
CPU負荷の高い関数の信頼性の高いベンチマークが必要な場合は、注意が必要です。
たとえば、2つの日付の差を計算する2つの関数を測定してみましょう。どちらが高速でしょうか?
このようなパフォーマンス測定は、しばしば「ベンチマーク」と呼ばれます。
// we have date1 and date2, which function faster returns their difference in ms?
function diffSubtract(date1, date2) {
return date2 - date1;
}
// or
function diffGetTime(date1, date2) {
return date2.getTime() - date1.getTime();
}
これら2つはまったく同じことを行いますが、一方ではミリ秒単位の日付を取得するために明示的なdate.getTime()
を使用し、もう一方は日付から数値への変換に依存しています。結果は常に同じです。
では、どちらが高速でしょうか?
最初のアイデアは、それらを何度も連続して実行し、時間差を測定することかもしれません。私たちの場合、関数は非常に単純なので、少なくとも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()
を使用する方がはるかに高速です!これは、型変換がないため、エンジンが最適化するのがはるかに簡単だからです。
さて、何かが得られました。しかし、これはまだ良いベンチマークではありません。
bench(diffSubtract)
の実行時にCPUが並行して何かを実行しており、リソースを消費していたと想像してみてください。そして、bench(diffGetTime)
を実行するまでに、その作業は完了しています。
最新のマルチプロセスOSでは、かなり現実的なシナリオです。
その結果、最初のベンチマークは2番目のベンチマークよりもCPUリソースが少なくなります。これは、間違った結果につながる可能性があります。
より信頼性の高いベンチマークのために、ベンチマークの全体のパックを複数回再実行する必要があります。
たとえば、次のようにします。
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;
// run bench(diffSubtract) and bench(diffGetTime) each 10 times alternating
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 );
最新のJavaScriptエンジンは、何度も実行される「ホットコード」にのみ高度な最適化を適用し始めます(めったに実行されないものを最適化する必要はありません)。したがって、上記の例では、最初の実行は十分に最適化されていません。ウォームアップランを追加することをお勧めします。
// added for "heating up" prior to the main loop
bench(diffSubtract);
bench(diffGetTime);
// now benchmark
for (let i = 0; i < 10; i++) {
time1 += bench(diffSubtract);
time2 += bench(diffGetTime);
}
最新のJavaScriptエンジンは多くの最適化を実行します。特に、演算子の動作や組み込み関数など、非常に小さなものをベンチマークする場合、「通常の使用法」と比較して「人工テスト」の結果を調整する場合があります。したがって、パフォーマンスを真剣に理解したい場合は、JavaScriptエンジンの仕組みを調べてください。そして、おそらくマイクロベンチマークはまったく必要ないでしょう。
V8に関する素晴らしい記事のパックは、https://mrale.ph にあります。
文字列からのDate.parse
メソッド Date.parse(str) は、文字列から日付を読み取ることができます。
文字列の形式は、YYYY-MM-DDTHH:mm:ss.sssZ
である必要があります。ここで、
YYYY-MM-DD
– は日付:年-月-日です。- 文字
"T"
は区切り文字として使用されます。 HH:mm:ss.sss
– は時刻:時、分、秒、ミリ秒です。- オプションの
'Z'
部分は、+-hh:mm
形式の時刻帯を示します。単一の文字Z
はUTC+0を意味します。
YYYY-MM-DD
や YYYY-MM
、さらには YYYY
のような、より短いバリアントも可能です。
Date.parse(str)
の呼び出しは、指定された形式で文字列を解析し、タイムスタンプ(1970年1月1日UTC+0からのミリ秒数)を返します。形式が無効な場合は、NaN
を返します。
例えば
let ms = Date.parse('2012-01-26T13:51:50.417-07:00');
alert(ms); // 1327611110417 (timestamp)
タイムスタンプからすぐに new Date
オブジェクトを作成できます
let date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );
alert(date);
まとめ
- JavaScriptの日付と時刻は、Date オブジェクトで表されます。「日付のみ」または「時刻のみ」を作成することはできません。
Date
オブジェクトは常に両方を含みます。 - 月はゼロからカウントされます(そうです、1月はゼロ月です)。
getDay()
の曜日は、ゼロからカウントされます(これは日曜日です)。Date
は、範囲外のコンポーネントが設定されている場合、自動的に修正されます。日/月/時を加算/減算するのに適しています。- 日付は減算でき、ミリ秒単位の差が得られます。これは、
Date
が数値に変換されるとタイムスタンプになるためです。 - 現在のタイムスタンプを高速に取得するには、
Date.now()
を使用します。
他の多くのシステムとは異なり、JavaScriptのタイムスタンプは秒単位ではなく、ミリ秒単位であることに注意してください。
より正確な時間測定が必要な場合があります。JavaScript自体にはマイクロ秒(1秒の100万分の1)単位で時間を測定する方法はありませんが、ほとんどの環境で提供されています。たとえば、ブラウザには、ページの読み込み開始からのミリ秒数をマイクロ秒精度(小数点以下3桁)で示す performance.now() があります。
alert(`Loading started ${performance.now()}ms ago`);
// Something like: "Loading started 34731.26000000001ms ago"
// .26 is microseconds (260 microseconds)
// more than 3 digits after the decimal point are precision errors, only the first 3 are correct
Node.jsには、microtime
モジュールやその他の方法があります。技術的には、ほとんどすべてのデバイスと環境でより高い精度を得ることができますが、それは Date
にはありません。
コメント
<code>
タグを使用し、複数行の場合は<pre>
タグで囲み、10行を超える場合はサンドボックス(plnkr、jsbin、codepen…)を使用してください。