⏰
dayjsでの西暦1000年より前の挙動について調べた
環境
- dayjs 1.10.4
状況
- 西暦1〜99年が、1901年〜1999年に勝手に変換され入力される
- 西暦100〜999年の変換でエラーになる (Firefoxのみ)
原因
1について
- 2桁数値は西暦1901〜1999年に変換されるというJavaScriptの仕様
-
dayjs('0099-01-01')
としたとき、内部ではnew Date(Date.UTC('0099', 0, 1, 0, 0, 0, '000'))
のように変換されている
この2つのあわせ技によって西暦1〜99年は1901〜1999年に変換されてしまう。
解法
無理やりやるとすれば、
const date = '0099-01-01'
dayjs(date).year(date.substr(0, 4))
とかか。
ただし、 MM-DD-YYYY
などの YYYY の位置が可変の環境を想定して対応するのは面倒か。
追記: 2021-04-14
そういえばECMA Script的にどうなってるんだろうと思って調べたところ、以下の箇所が該当箇所の模様。
ここの 5.j.ii で定義されている模様。
2について
const dayjs = require('dayjs');
const utc = require('dayjs/plugin/utc');
const timezone = require('dayjs/plugin/timezone');
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs('0999-01-01').startOf('hour'); // OK
dayjs('0999-01-01').tz('Asia/Tokyo').startOf('hour'); // NG
APIとのやり取りの都合上、クライアントの時刻をUTCに変換した日時を送信する必要があったため、 dayjs での変換を噛ませていた。
通常であれば問題がない dayjs().startOf('xxx')
が、 timezone
プラグインを使用したときにエラーを吐くようになっていた。
dayjsのソースを追っていくと startOf('xxx')
は toDate('s')
を呼び出しており、その toDate('s')
は dayjs(this.format('YYYY-MM-DD HH:mm:ss:SSS')).toDate()
を行っていた。
format()
の内部では JavaScript の Date().getFullYear()
を呼び出しており、 Date().getFullYear()
は数値で年を返す。そのため、文字列 '0999'
は数値 999
として解釈され、 format()
の結果としては '999-01-01'
が返る。しかし、Firefoxは '999-01-01'
という値を有効な日付として解釈できないため、 Invalid Date
になってしまう。何を言っているんだお前は。
refs.
解法
こんな感じで行けるか?(未確認)
const isoString = dayjs('0999-01-01').tz('Asia/Tokyo').toISOString();
dayjs(isoString).startOf('hour').utc().format('YYYY-MM-DDTHH:mm:ss.SSSZ');
Discussion