新しいことにはウェルカム

技術 | 電子工作 | ガジェット | ゲーム のメモ書き

JavaScriptの日付演算ライブラリMoment.jsの使い方メモ

JavaScriptで日付演算をする際、組み込みのDate関数は扱いが難しいため、一般的に何かしらのライブラリを使います。

ライブラリは、よく見かける「Moment.js」を使っています。

何度やっても使い方を忘れるし、同じ所でハマっているので、自分用使い方メモを残します。

インストール・読み込み

日本時間(JST)をよく使うので、サーバーのロケールを設定しなくても動くよう、moment-timezoneを使っています。

インストール

npm install moment-timezone

読み込み

import * as moment from 'moment-timezone';
moment.tz.setDefault('Asia/Tokyo'); // タイムゾーンをJSTに設定

// TypeScript用型
// 関数の引数に渡す時に指定すると補完してくれるので便利
import { Moment } from 'moment-timezone';

// 型使用例
function test(date: Moment){

}

生成・出力

設定したタイムゾーンは

  • オフセットを指定しなかった時の「生成する時の日付」
  • 「出力する時の日付」

に影響します。

出力は設定したタイムゾーンになるのですが、ピンポイントでUTC出力したい時は、moment.utc()で一時的にUTCに変換して出力します。

import * as moment from 'moment-timezone';
moment.tz.setDefault('Asia/Tokyo'); 

// 現在時刻を取得
const date_now = moment(); 

// 出力はJSTになる
date_now.format('YYYY-MM-DD HH:mm:ss');

// 値を全て指定して作成
// オフセットを指定しない入力値はJSTとして扱われる
const date_jst = moment('2019-01-01 00:00:00.000');  

// フルフォーマット出力
// 入力値がJSTとして扱われている
date_jst.format('YYYY-MM-DD HH:mm:ss.SSSZ');
// 2019-01-01 00:00:00.000+09:00

// 入力値にオフセットを指定できる(例:UTC)
// msの指定は無くても構わない
const date_utc = moment('2019-01-01 00:00:00.000+00:00');  

// 入力はUTCにしたので、出力のJSTは9時間進んで9時になっている
date_utc.format('YYYY-MM-DD HH:mm:ss.SSSZ');
// 2019-01-01 09:00:00.000+09:00

// UTCに変換して出力
// 入力はUTCだったので、出力も同じ値になっている
date_utc.utc().format('YYYY-MM-DD HH:mm:ss.SSSZ');
// 2019-01-01 00:00:00.000+00:00

特殊フォーマットの文字列から生成

日付文字列が「YYYY-MM-DD HH:mm:ss」形式でない場合は、2番目の引数にフォーマットを指定してあげることで、生成することができます。

例えば、TwitterのAPIで取得される日付は「Fri Mar 05 14:11:26 +0000 2010」といった形式なのですが、それからmomentを生成するには下記のようになります。

const date_twitter = moment(
    'Fri Mar 05 14:11:26 +0000 2010',
    'ddd MMM DD HH:mm:ss ZZ YYYY'
);
console.log(date_twitter.utc().format('YYYY-MM-DD HH:mm:ss'));
// 2010-03-05 14:11:26

同様の方法で、unixtimeからの生成も可能です。

フォーマットの記述方法は出力のものと同じで、下記のtokensに説明があります。

format

moment.format()で指定したフォーマットで文字列出力します。

とりあえずフルフォーマットをメモしておいて、必要に応じて切り出して使うといいんじゃないでしょうか。

momentを作成するには、最低限「年月日」の情報があればできます。

出力は、満たない桁はゼロ埋めにして、「年月日」間は「-」でつなぐフォーマットにしておくと、ソートがしやすいし、データベースにロードする時にエラーになりにくいのでオススメです。

const date = moment('2019-01-01');
// フルフォーマット
datel.format('YYYY-MM-DD HH:mm:ss.SSSZ');
// 2019-01-01 00:00:00.000+09:00

要素の取得・変更はformat経由で行う

moment.~()で値を取得。moment.~(<number>)で値を変更できるのですが、挙動にクセがあるので、それらは使わず、 多少手間ですが、format経由で行うようにした方がミスが少ないです。

挙動のクセ

  • moment.month()は月を示しますが、範囲は「1~12」ではなく「0~11」です
  • 月の日付はmoment.day()ではなくmoment.date()です

これらのクセを知っていれば問題ないのですが、しばらく使っていないとついつい忘れてしまうので、割り切って、要素取得・変更関数は使わないようにしています。

format経由での取得・変更

const date = moment('2019-02-03');

// 取得
// formatでString出力して型変換する
Number.parseInt(date.format('M');
// 月:範囲「1~12」

// 変更
// formatでString出力して、そのStringでmomentを作りなおす
const date_last_year = moment(date.format('2018-MM-DD'));

曜日取得

例外として、週を求める時はmoment.isoWeekday()を使っています。

「月曜~日曜」が数字の「1~7」になります。

const date = moment('2019-01-01');
date.isoWeekday());
// 1(月曜)

演算の注意

Moment.jsの演算はイミュータブルではありません。

オブジェクトを演算すると、オブジェクトの値が変更されてしまいます。

const date_original = moment('2019-01-01');
const date_result = date_original.add(2, 'day');

date_result.format('YYYY-MM-DD'));
// 2019-01-03

date_original.format('YYYY-MM-DD');
// 2019-01-03
// date_originalが加算されている

momentの代入は参照のコピーのため、コピーを変更すると、参照元も変更されます。

const date_original = moment('2019-01-01');
const date_ref = date_original;

date_ref.add(2, 'day');

date_original.format('YYYY-MM-DD');
// 2019-01-03
// date_originalが加算されている

このあたりの挙動、ついつい忘れてハマることが多いので、演算する時は、moment.clone()でコピーを作成して、コピーを使って演算するようにした方がミスが少ないです。

const date_original = moment('2019-01-01');

// コピー作成
const date_copy = date_original.clone();

// 演算
const date_add = date_copy.add(2, 'day');

date_add.format('YYYY-MM-DD');
// 2019-01-03

date_original.format('YYYY-MM-DD');
// 2019-01-01
// date_originalが元のまま

演算で使用する単位

Key
year
month
day
hour
minute
second
millisecond

差分(diff)

差分の単位を指定できます。

const date_a = moment('2020-01-01');
const date_b = moment('2019-01-01');

date_a.diff(date_b, 'year');  // a - b
// 1

加算・減算

減算関数は別途あるのですが、加算にマイナス値を使っても行えるので、その方が覚えることが少なくて済みます。

const date = moment('2019-01-01');

date.add(1, 'day'); // 2019-01-02
date.add(-1, 'day'); // 2018-12-31

比較

moment.diff()で単位を指定しなかった場合は、ms単位(最小単位)での差を出します。

これを使って、momentどうしの比較を行います。

const date_a = moment('2019-01-01');
const date_b = moment('2020-01-01');

if(date_a.diff(date_b)>0){
   // date_a>date_b
}

if(date_a.diff(date_b)<0){
   // date_a<date_b
} 

if(date_a.diff(date_b)===0){
   // date_a=date_b
} 

しかし、同じ日かのチェックなど、厳密に比較をしたいわけではない時もあります。

Moment.jsは、そういった単位を切り詰めての演算手段が用意されていないため、一旦formatでStringにして単位を切り詰めて演算を行うのがいいです。

const date_a = moment('2019-01-01');
const date_b = moment('2020-01-01');
const str_a = date_a.format('YYYY-MM-DD');
const str_b = date_a.format('YYYY-MM-DD');

if(str_a>str_b){
   // date_a>date_b
}

if(str_a<str_b){
   // date_a<date_b
} 

if(str_a===str_b){
   // date_a=date_b
} 

月末の日付取得

月末の日付取得は、月初から1日引いてもいいのですが、moment.endOf('month')を使うと楽です。

ただし、時間は12時なっているので注意が必要です。

const date = moment('2019-01-01');

const date_end_of_month = date.endOf('month');
date_end_of_month.format('YYYY-MM-DD hh:mm:ss');
// 2019-01-31 11:59:59 

感想など

こういった自分がよく使うコードは、既存コードからコピペしたり、Evernoteにメモしたりしているのですが、経緯・過程を端折っていたりして、パッと見ても「何だっけ?」となることがあります。

こうやって記事にすれば、何でそうなっているのかも書くので、後で自分で使う分にもいいかも。

関連カテゴリー記事

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com

www.kwbtblog.com