JavaScript
日付計算
More than 3 years have passed since last update.

Dateといっても、異性とするほうじゃなくて(そっちも地雷原と化すかもしれませんが)、JavaScriptのDateオブジェクトの方です。もともと日付計算はややこしいものですが、JavaScriptのDateオブジェクトが持っているクセもあって、さらにハマりどころが増えてしまっています。


四変化コンストラクタ

MDNのページにもDateコンストラクタの解説が載っていますが、まず最初の表記からしてあまり見慣れません。


Date-constructor

new Date()

new Date(value)
new Date(dateString)
new Date(year, month, day [, hour, minute, second, millisecond])

関数オーバーロードできるC++なら、コンストラクタが複数あっても気に留めるほどではないかもしれません。しかし、ここはJavaScriptの世界ですし、



  • new Date()は、現在の時刻を表現するDateオブジェクトになる


  • new Date(value)(引数が数値)は、Unix Timeをミリ秒単位で表現したものとして引数を解釈して、その時間のDateオブジェクトになる


  • new Date(dateString)(引数が文字列)は、文字列をパースして、その時間になるDateオブジェクトを返す

  • 最後のパターンでは、日付の各要素を指定してDateを生成する

とまあ、機能もバラバラです。


YMD三兄弟

Dateオブジェクトの値を取得・設定するメソッドもいろいろありますが、これまた一筋縄では行きません。



  • getYear()は、(最近の実装では)年から1900を引いたものを返します。2000年問題丸出しのこんなものは使わず、getFullYear()/setFullYear()を使いましょう。


  • getMonth()は、1月が0、2月が1…12月が11と、1少ない値を返します。ヨーロッパの言語などで月の名前を配列に入れるシチュエーションではこの方が便利かもしれませんが、算用数字で月を現す日本では引っかかりやすくなっています。なお、コンストラクタやsetMonth()も同じく0オリジンです。


  • getDay()曜日を返すメソッドです。日付の操作にはgetDate()/setDate()を使いましょう。


翌月…って?

setFullYear()setMonth()setDate()は、範囲外の値を入れても、勝手に繰り上げてくれる(3月のオブジェクトでsetDate(33)とすると4月2日になる)のは便利ですし、getXXX()系のメソッドと組み合わせれば相対移動も可能ですが、注意点があります。


next_month.js

d=new Date();

d.setMonth(d.getMonth()+1);
m=d.getMonth();

このように書くと、大半の場合は問題ないのですが、1月30・31日、3月31日、5月31日などでは、翌月に対応する日がないため、さらに翌月まで進んでしまいます。月にしか興味がないシチュエーションであれば、d,setDate(1)のように日付を安全なものに変えておきましょう。


Dateのコピー

Dateに限ったことではありませんが、JavaScriptではオブジェクトを別な変数へ代入してもコピーが作られず、同じオブジェクトを指し続けます。

foo=new Date();

bar=foo;
foo.setDate(5); //barも同じく変化する

Dateをコピーしたい場合、bar=new Date(foo.getTime())1のように、既存のDateから時間値を取り出して、それをコンストラクタに投げることで、同じ値を持った新規オブジェクトを作ることができます。


関連記事

JavaScript の Date は罠が多すぎる





  1. bar=new Date(foo)のようにも書けます。この形でも、fooはいったんプリミティブに変換されて数値となっているので、動作としては同じですし、コピーコンストラクタにも見えるので意味を取りづらいこともないでしょう。