たれぱんのびぼーろく

わたしの備忘録、生物学とプログラミングが多いかも

Javascriptの日時 (date & time)

Javascript標準で日時を扱うにはDateオブジェクトを使う.
使い方は割と簡単で、いくつか問題点はあるけどとりあえず使う分にはまぁ

概要

こんな風になっている
f:id:tarepan5884:20170910222214p:plain

inputはローカル時間 (日本時間とかアメリカ時間とか。時差のようなもの) の情報を持っているが、気にせずDateオブジェクトに入れてしまう.
Dateオブジェクトがローカル時間を良い感じに扱ってくれ、内部ではローカル時に影響されないUnix時間という時間表現で時間を保持している.
Dateオブジェクトから日時情報を取り出す際には様々な書式を設定でき、ISO8601表記や、月(month)のみの取り出しなどが出来る.ローカル時間に合わせた表記ももちろんできるよ、優秀!

  • input:
    • コンストラクタ: new Date(args)
    • setter: dateObj.setMonth()など
  • internal: Unix時間
  • output:
    • 全情報
      • Unix時間: Date.getTime()
        • => 1505049236671
      • 地域時間: Date.toLocaleString()
        • => "2017/9/10 22:48:41"
      • ISO8601-UTC: Date.toISOString()
        • => "2017-09-10T13:48:41.083Z"
      • ISO8601-localTime: なんと、無い!!
      • JSON用: Date.toJSON()
    • 個別の年、月、日など
      • dateObj.getMonth(): 月(month)のみ返してくる
      • etc.etc.

コンストラクタや各種setter, getterはググれば簡単にわかる。上記の全体像が大事.

良く上げられる問題点

  • getterの返り値が直感的ではない
    • 1月が"0"で返ってくる。ゆえに9月は8!! ミスリーディング!!
  • 実装依存の部分が非常に多い
    • 仕様書を見る限り、最近はかなり標準化されているみたい
    • 古いブラウザ使うなら機能豊富なライブラリを使うべし
  • ISO8601のローカル時間表記が出来ない
    • ローカルにかかわる情報もってんだろおんどれ、もうちとがんばれ!!

オブジェクト内部の時間表現

以下は興味のある人向け

Dateオブジェクト内ではUnix時間で日時が表現されている.

A Date object contains a Number indicating a particular instant in time to within a millisecond. Such a Number is called a time value. A time value may also be NaN, indicating that the Date object does not represent a specific instant of time.
Time is measured in ECMAScript in milliseconds since 01 January, 1970 UTC.
https://www.ecma-international.org/ecma-262/6.0/#sec-time-values-and-time-range
https://www.ecma-international.org/ecma-262/7.0/#sec-time-values-and-time-range
* ECMAScript 2015 ~ 2018-draft まで 20.3.1.1Time Values and Time Range に変更なし

ECMAScript仕様

(ECMAScript 2015, 2016, 2017, 2018-draft)
20.3 Date Objects (https://tc39.github.io/ecma262/#sec-date-objects)にて詳しく規定

変更履歴

language specification 20.3をdiff

2015 -> 2016

20.3.1.16 Date Time String Formatだけ変更点がありそう
ほかは表記上の変更のみにみえる

2016 -> 2017

20.3.3.4に変更点

2017 -> 2018-draft

20.3.4.41-42に大幅変更?

内部動作

DateValue internal slotが全ての肝.
DateValueはDateインスタンスの持つ内部時間表現。Unix時間(1970-01-01T00:00zからの経過ミリ秒)で表現されている.
コンストラクタもsetterも、いろいろな入力をUnix時間へ変更してDateValueに入れる仕事をしている.
getterはどれもUnix時間を適切な出力フォーマットへ変換する仕事をしている.

読み解くのに重要な内部関数

thisTimeValue(value)  
/*
 * @argument {Object} value - argument object
 * @return {?} - value.[[DateValue]] || Type Error exception
 */
// 20.3.4

"this time value"は thisTimeValue(this)の返り値 (20.3.4)
Date prototype objectはDateValue internal slotを持たない(20.3.4)
インスタンスが初期化されたとに DateValue == time valueになるもよう (20.3.4)

20.3.1.15 TimeClip (time): time => NaN || parseInt(time)

MakeDate() -> TimeClip(UTC(finalDate)) -> DateValue
ほとんどのsetterでは、setterコール時にUnix時間からYear等を再計算し、setterの引数と再計算値を用いて(internalな)MakeDate()を行ってUnix時間へ再変換してる.