moment.jsに代わる日付ライブラリDay.js
moment.jsについて調べ物をしていたら、moment.jsはもう使わない方がいいという内容をわくわくBankさんのページで見つけました。
設計の部分で時代にそぐわない作りになっていて、現在それを利用しているユーザーのコードを崩すことなくアップデートさせるのは不可能だからといったようなことが、Moment.js公式:「Project Status」に書かれていました。
そこで先のふたつのサイトで代替として紹介されていた日付ライブラリの一つDay.jsを採用してみることにしました。
ここではNode.jsやブラウザにおける、Day.jsの基本的な使い方と日本語ロケールの設定、バリデーション(日付チェック)、曜日の取得を行う方法を書き残しておきます。
インストールと初期設定
まず、Day.jsの公式ページに従ってNode.jsでのインストールします。ロケールとプラグインも一緒にインストールされます。
npm install dayjs
...
CommonJSでrequireする際は次のように書きます。
const dayjs = require('dayjs');
...
またES2015(以降)の場合は次のようになります。
import dayjs from 'dayjs';
...
ブラウザで利用する場合は、公式サイトからダウンロードして展開したファイル群からjsファイルを抽出してローカルサイトで配置することも可能ですが、CDNから探した方が分かりやすいかもしれません。jsDeliverから読み込む例は次の通りです。
この時、読み込まれた内容はwindow.dayjsに格納されます。
基本的な使い方
Node.jsの引数に開始日(YYYYMMDD)と終了日(YYYYMMDD)を渡す前提で、基本的な使い方の紹介を兼ねた日付チェックコードを紹介します。
インポートしたdayjsで、引数なしでインスタンスを作成すると現在時刻のDay.jsインスタンスが取得できます。
インスタンスからaddメソッドを呼ぶと日付の加算、減算ができます。ここでは引数に1と'day'を指定して1日後のインスタンスを取得しています。
formatメソッドでは引数にフォーマット文字列を指定することで、日付文字列を取得できます。ここでは、時分秒マイクロ秒をカットしたインスタンスをdayjsInstanceTodayとして作成しています。dayjsのコンストラクタに、文字列とフォーマットを指定してやることで指定しています。
例示としてNode.js環境でコードを紹介しますが(process.argvの部分)、dayjs()でインスタンスを作成したり、インスタンスからaddやisValidメソッドを呼んだりする部分はブラウザでも可能です。
let dayjsInstanceFrom=dayjs();
let dayjsInstanceTo=dayjs(dayjs().add(1,'day').format('YYYYMMDD'));
let dayjsInstanceOldest = dayjs().add(-1,'month');
let dayjsInstanceToday = dayjs(dayjs().format('YYYYMMDD'),'YYYYMMDD');
...
日付をNode.jsの実行時の引数から受け取っとてバリデーションをします。ここでは引数にYYYYMMDDが入っている前提とし、それをDay.jsのコンストラクタに渡しています。
正式にインスタンスが作成できたかどうかは、isValidメソッドで判定できます。注意しないといけないのは、20210230といった値を設定した際に、内部的には20210302を設定したのと同じことになり、isValidはtrueになります。なので日付の入力値をチェックする際は一旦formatして元の文字列と一緒になるか確認します。
ちなみに、Node.jsのコマンドライン引数はprocess.argvのインデックス2から入ってきます。例えば「node somecode.js param1 param2」とした際、param1はprocess.argv[2]、param2はprocess.argv[3]に入ってきます。
//...(続き)...
if (2 < process.argv.length) {
dayjsInstanceFrom=dayjs(process.argv[2],'YYYYMMDD')
if (dayjsInstanceFrom.isValid()===false || dayjsInstanceFrom.format('YYYYMMDD')!==process.argv[2]) {
console.log("開始日に指定された日付が不正です:"+process.argv[2]);
return;
}
}
if (3 < process.argv.length) {
dayjsInstanceTo=dayjs(process.argv[3],'YYYYMMDD')
if (dayjsInstanceTo.isValid()===false || dayjsInstanceTo.format('YYYYMMDD')!==process.argv[3]) {
console.log("終了日に指定された日付が不正です:"+process.argv[3]);
return;
}
}
よくあるチェックに、開始日と終了日の入れ違いをみるものがあります。これはisBeforeを使って実現することができます。メソッドを呼び出すインスタンス(ここではdayjsInstanceTo)が、指定したインスタンスより前になっていれば、trueが返ります。
ここで第2引数に'day'を渡しています。引数がない場合比較はミリ秒単位で行われるのをここでは、日付単位で比較しています。日付が同じかチェックするにはisSameメソッドが用意されています。diffでは差を求めることができます。開始日<終了日の時、終了日.diff(開始日)でプラスの値が戻ります。これを逆にした場合はマイナスの値になります。
//...(続き)...
//デフォルトはミリ秒での比較なので、dayを指定します
if (dayjsInstanceTo.isBefore(dayjsInstanceFrom,'day')) {
console.log("終了日が開始日より前になっています");
return;
}
if (dayjsInstanceTo.isSame(dayjsInstanceFrom,'day')) {
console.log("開始日と終了日が同じ日になっています");
return;
}
if (dayjsInstanceFrom.isBefore(dayjsInstanceOldest,'day')) {
console.log("日付の指定が古すぎます");
return;
}
if (30 < dayjsInstanceTo.diff(dayjsInstanceFrom,'day')) {
console.log("日付の範囲は30日以内にしてください");
return;
}
拡張ライブラリ
先のisBeforeとisSameでの比較の処理が冗長なのでまとめて処理できないのかと思いました。diffで数値化して判定するのもいいのですが、ここでは拡張ライブラリの「isSameOrBefore」というメソッドを実装してみたいと思います。
行頭でライブラリを読み込み、それをdayjs.extendの引数として渡してやることで拡張ライブラリの使用が可能になります。
const sameOrBefore = require('dayjs/plugin/isSameOrBefore');
dayjs.extend(sameOrBefore);
if (dayjsInstanceTo.isSameOrBefore(dayjsInstanceFrom,'day')==false) {
console.log("終了日は開始日より後の日付にしてください");
return;
}
ブラウザでは次のように拡張ライブラリ別に存在するjsファイルをscriptタグで読み込みます。
この時、window.dayjs_plugin_isSameOrBeforeに読み込まれるので、window.dayjsの.extendメソッドにそれを渡します。
<script>https://cdn.jsdelivr.net/npm/dayjs@1.11.5/plugin/isSameOrBefore.js</script> dayjs.extend(dayjs_plugin_isSameOrBefore);
ライブラリが分かれているのは軽量化のためだと思います。他の拡張ライブラリには、日付が指定した範囲ないにあるかを調べる「isBetween」や、うるう年の判定をする「isLeapYear」などがあります。
すべてを調べたわけではありませんが、ブラウザのスクリプトタグで読み込んだ場合それらは、dayjs_plugin_ライブラリ名という名前でwindowに設定されるようです。「 TypeError: t is not a function 」のエラーがブラウザのコンソールに出力された場合はextendsに渡したオブジェクトが不正な可能性があります。オブジェクト名があっているか、間違えて文字列を渡したりしていないかチェックしましょう。
日本語ロケール
日本語のロケールデータはDay.jsのインストールには含まれるのですが、利用するには拡張の設定が必要です。ここでは曜日を出してみようと思います。
日本語だけでなく全てのロケールデータが拡張ライブラリとなっているため先に設定します。ロケールデータの拡張を設定すると、Day.jsのインスタンスのlocaleData()から関連のメソッドを呼び出すことができます。
const localeData = require('dayjs/plugin/localeData');
dayjs.extend(localeData);
console.log(dayjs().localeData().monthsShort());
/*
[
'Jan', 'Feb', 'Mar',
'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep',
'Oct', 'Nov', 'Dec'
]
*/
英語になっているロケールを日本語にします。日本語のロケールを読み込むには、require('dayjs/locale/ja')とします。この後の書き方で、ロケールを全体に設定することも個別(インスタンス毎)に設定することもできます。
const localeData = require('dayjs/plugin/localeData');
dayjs.extend(localeData);
require('dayjs/locale/ja'); //CommonJS
// import 'dayjs/locale/jp' //ES2015
...
//全体に適用する
dayjs.locale('ja');
console.log(dayjs().localeData().months());
/*
[
'1月', '2月', '3月',
'4月', '5月', '6月',
'7月', '8月', '9月',
'10月', '11月', '12月'
]
*/
...
//特定のインスタンスに適用する。
let some = dayjs().locale('ja');
console.log(some.localeData().weekdays());
/*
[
'日曜日', '月曜日',
'火曜日', '水曜日',
'木曜日', '金曜日',
'土曜日'
]
*/
ブラウザでは、ロケール拡張はwindow.dayjs_plugin_localeDataに読み込まれます。日本語ロケールはwindow.dayjs_locale_jaに読み込まれますが、セットの仕方はNode.jsと同じように「ja」を指定するだけです。
<script>https://cdn.jsdelivr.net/npm/dayjs@1.11.5/plugin/localeData.js</script> <script>https://cdn.jsdelivr.net/npm/dayjs@1.11.5/locale/ja.js</script> dayjs.extend(dayjs_plugin_localeData); dayjs.locale('ja');
曜日拡張ライブラリ
曜日の取得も拡張ライブラリを設定する必要があります。設定後はインスタンスのweekdayメソッドから曜日のインデックスを取得できます。この値と先のロケールデータを組み合わせて、曜日の文字列を取得することができます。
const weekday = require('dayjs/plugin/weekday');
dayjs.extend(weekday);
let aday = dayjs('2021-04-30','YYYY-MM-DD').locale('ja');
console.log(aday.localeData().weekdaysShort()[aday.weekday()]);
//金
Dateオブジェクトとの互換性
JavaScriptのDateオブジェクトからDay.jsのオブジェクトへの変換はDateオブジェクトをDay.jsのコンストラクタに渡すだけで変換できます。
let dateObj = new Date(2021, 5, 8);
let dayjsObj = dayjs(dateObj);
逆に、Day.jsのオブジェクトからDateオブジェクトを取得するには、toDateメソッドを使います。また、$dプロパティにも値が入っているようです。
console.log(dayjsObj.toDate());
console.log(dayjsObj.$d);