JSのDate型について気になったので改めて確認した。
JSのDateはTimezoneを持っている。
const date = new Date()
このdateをコンソールで確認すると以下になる。
Wed May 01 2024 21:53:33 GMT+0900 (日本標準時)
Dateインスタンスをシリアライズする場合は、ISO 8601に従う。JSON.stringifyもそうなっている。
JSON.stringify({date1: date, date2: date.toISOString()})
は以下になる。
{ "date1":"2024-05-01T12:55:37.125Z", "date2":"2024-05-01T12:55:37.125Z" }
JSのDateインスタンスはタイムゾーン情報を保持するので、getTime()やtoISOStringで使う限りは、 クライアント、サーバサイドのタイムゾーンが異なる環境において、どこでnew Date()しても問題はない。
ISO 8601形式の時刻の文字列をDateインスタンスに戻すには、以下でよい。
new Date("2024-05-01T12:55:37.125Z")
インスタンスにする環境のタイムゾーンがJSTであれば以下に戻る。
Wed May 01 2024 21:53:33 GMT+0900 (日本標準時)
集計時
日毎のアクセス数を集計したいとする。 アクセスログから、アクセス数を日毎に集計する場合には、通常アクセスログのタイムスタンプを日時に丸め(切り落とし)て、集計する。 この時、データベースサーバにどのタイムゾーンでデータが保存されているかはよく確認しておかないと、集計される範囲がずれてしまうことがある。
たとえば、ORマッパーのprismaを使うとJSのDateインスタンスはUTCに直して保存される。 そのため、SQLで集計する場合は、タイムゾーンを適用する必要がある。
例えば、SQLサーバの場合の集計用のSQLを書いてみると以下になる。
select format(dateadd(hh, 9, time), 'yyyy-MM-dd') as date , email , count(*) as value from access group by format(dateadd(hh, 9, time), 'yyyy-MM-dd'), email order by date
このSQLでは、timeカラムの値をdateadd(hh, 9, time)
でUTCから日本時間に変換し、その後format(dateadd(hh, 9, time), 'yyyy-MM-dd')
で日時文字列に変換している。
これらの関数は方言があるのでデータベース毎に変更する必要はある。