今らなら immutable で軽くて i18n にも対応してるってことで date-fns 使うのが良いのだろうけど、moment.js の i18n 対応でいろいろあったので、一応メモっておく。
ペルシア語の format() メソッドに注意
例えば、API向けに投げる日付データとして、YYYY-MM-DD な値を用意する場合、日本語や英語環境なら普通に format() メソッドを使えば問題はない。
// 日本語
moment.locale('ja');
console.log('lang', lang);
console.log(moment().format('YYYY-MM-DD'));
// -> 2018-02-05
// 英語
moment.locale('ja');
console.log('lang', lang);
console.log(moment().format('YYYY-MM-DD'));
// -> 2018-02-05
ところが、同じことをペルシア語でやるとペルシア数字に変換されちゃう。
// ペルシア語
moment.locale('fa');
console.log('lang', lang);
console.log(moment().format('YYYY-MM-DD'));
// -> ۲۰۱۸-۰۲-۰۵
画面に表示する場合はこれでよいけど、APIに投げるデータとしては問題がある。なので moment.localeData().preparse() を使って YYYY-MM-DD に変換する。
// ペルシア語
moment.locale('fa');
console.log('lang', lang);
console.log(moment.localeData().preparse(moment().format('YYYY-MM-DD')));
// -> 2018-02-05
// 日本語
moment.locale('ja');
console.log('lang', lang);
console.log(moment.localeData().preparse(moment().format('YYYY-MM-DD')));
// -> 2018-02-05
各言語毎の言葉で月を求めるには months() メソッド
これは知ってると便利そうなのでメモ。
// 日本語
moment.locale('ja');
moment.months();
// -> ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
// 英語
moment.locale('en');
moment.months();
// -> ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
// ペルシア語
moment.locale('fa');
moment.months();
// -> ["ژانویه", "فوریه", "مارس", "آوریل", "مه", "ژوئن", "ژوئیه", "اوت", "سپتامبر", "اکتبر", "نوامبر", "دسامبر]
fromNow() 「○ヶ月前」を求める時のデフォルト閾値に注意
fromNow() メソッド使うと「○ヶ月前」的な文字列が求められる。
console.log(moment().subtract(25, 'day').fromNow());
// -> 25日前
だけど26日前にすると...
console.log(moment().subtract(26, 'day').fromNow());
// -> 1ヶ月前
なぜか26日前が1ヶ月前扱いになってしまう。
relativeTimeThreshold とかいう閾値を設定したり、求めたりするメソッドがある。
duration.humanize has thresholds which define when a unit is considered a minute, an hour and so on. For example, by default more than 45 seconds is considered a minute, more than 22 hours is considered a day and so on. To change those cutoffs use moment.relativeTimeThreshold(unit, limit) where unit is one of ss, s, m, h, d, M.
デフォルトでは45秒以上が1分とみなされ、22時間以上は1日とみなされるらしい。
他のデフォルトも見てみると....
console.log(moment.relativeTimeThreshold('ss')); // 44
console.log(moment.relativeTimeThreshold('s')); // 45
console.log(moment.relativeTimeThreshold('m')); // 45
console.log(moment.relativeTimeThreshold('h')); // 22
console.log(moment.relativeTimeThreshold('d')); // 26
console.log(moment.relativeTimeThreshold('M')); // 11
うーん、こんなんなってるのか...
閾値を調整して再度試してみる。
moment.relativeTimeThreshold('ss', 60);
moment.relativeTimeThreshold('s', 60);
moment.relativeTimeThreshold('m', 60);
moment.relativeTimeThreshold('h', 24);
moment.relativeTimeThreshold('d', 30);
moment.relativeTimeThreshold('M', 12);
console.log(moment().subtract(25, 'day').fromNow());
// -> 25日前
console.log(moment().subtract(26, 'day').fromNow());
// -> 26日前
ちゃんと26日になった。
念のため、3ヶ月先ぐらいまでチェックしてみる、1ヶ月づつ遡って前後5日間をチェック。
moment.relativeTimeThreshold('ss', 60);
moment.relativeTimeThreshold('s', 60);
moment.relativeTimeThreshold('m', 60);
moment.relativeTimeThreshold('h', 24);
moment.relativeTimeThreshold('d', 30);
moment.relativeTimeThreshold('M', 12);
console.log('now', moment().format('YYYY/MM/DD'));
Array(3).fill(null).forEach((_, i) => {
console.log('----------');
// 1ヶ月先の -5 〜 +5 日間をチェック
const base = moment().subtract(i + 1, 'months').add(5, 'day');
Array(10).fill(null).forEach(() => {
const d = base.subtract(1, 'day');
console.log(d.format('YYYY/MM/DD'), d.fromNow());
});
});
ん、なんか変。
now 2018/02/06
----------
2018/01/10 27日前
2018/01/09 28日前
2018/01/08 29日前
2018/01/07 1ヶ月前
2018/01/06 1ヶ月前
2018/01/05 1ヶ月前
2018/01/04 1ヶ月前
2018/01/03 1ヶ月前
2018/01/02 1ヶ月前
2018/01/01 1ヶ月前
----------
2017/12/10 2ヶ月前
2017/12/09 2ヶ月前
2017/12/08 2ヶ月前
2017/12/07 2ヶ月前
2017/12/06 2ヶ月前 <-- この辺から2ヶ月前でないの?
2017/12/05 2ヶ月前
2017/12/04 2ヶ月前
2017/12/03 2ヶ月前
2017/12/02 2ヶ月前
2017/12/01 2ヶ月前
----------
2017/11/10 3ヶ月前
2017/11/09 3ヶ月前
2017/11/08 3ヶ月前
2017/11/07 3ヶ月前
2017/11/06 3ヶ月前 <-- この辺から3ヶ月前でないの?
2017/11/05 3ヶ月前
2017/11/04 3ヶ月前
2017/11/03 3ヶ月前
2017/11/02 3ヶ月前
2017/11/01 3ヶ月前
どうも、relativeTimeRounding() って言うメソッドで指定できるコールバック処理のデフォルト処理が、formNow()メソッドで返す日付を四捨五入しちゃってるらしい(1.6ヶ月的な数値を2ヶ月にしちゃってる)。
と、SallyAcolyte さんが教えてくれた(ありがとうございます!)
なので、以下の様に Math.floor使って、端数を切り捨てるようにする。
moment.relativeTimeRounding(Math.floor);
あらためて実行!
now 2018/02/06
----------
2018/01/10 27日前
2018/01/09 28日前
2018/01/08 29日前
2018/01/07 1ヶ月前
2018/01/06 1ヶ月前
2018/01/05 1ヶ月前
2018/01/04 1ヶ月前
2018/01/03 1ヶ月前
2018/01/02 1ヶ月前
2018/01/01 1ヶ月前
----------
2017/12/10 1ヶ月前
2017/12/09 1ヶ月前
2017/12/08 1ヶ月前
2017/12/07 1ヶ月前
2017/12/06 2ヶ月前 <-- ここから2ヶ月前
2017/12/05 2ヶ月前
2017/12/04 2ヶ月前
2017/12/03 2ヶ月前
2017/12/02 2ヶ月前
2017/12/01 2ヶ月前
----------
2017/11/10 2ヶ月前
2017/11/09 2ヶ月前
2017/11/08 2ヶ月前
2017/11/07 2ヶ月前
2017/11/06 3ヶ月前 <-- ここから3ヶ月前
2017/11/05 3ヶ月前
2017/11/04 3ヶ月前
2017/11/03 3ヶ月前
2017/11/02 3ヶ月前
2017/11/01 3ヶ月前
良さそう!