ISO 形式は各ブラウザをはじめとして多くの環境で共通して用いることのできる日時の形式です。このため、なるべく ISO 形式で日時をやりとりしたくなります。
JavaScript の Date オブジェクトには ISO 形式の文字列を返す toISOString というメソッドがあります。このメソッドを用いることで色々した Date オブジェクトを ISO 形式の文字列に変換することができます。
Date.prototype.toISOString() – JavaScript | MDN
この toISOString メソッドですが、タイムゾーンを必ず UTC として表示するという特徴があります。これは場面によっては便利やもしれませんが、ユーザーのシステム上のタイムゾーン情報を消してしまうという欠点でもあります。このためロケール付きの ISO 形式の文字列を作る関数の需要があります。
console.log(new Date().toISOString());// 2022-11-29T02:05:33.874Z 必ず←になるが console.log(new Date().toISOString());// 2022-11-29T11:05:33.874+09:00 ←の様になってほしい場合がある
これを実現する方法はいくつもあります。この記事では三つ紹介します。
一つ目はライブラリを使う方法です。よく使われているライブラリである moment(既に非推奨のライブラリではありますが、既存プロジェクトで使われていることも非常に多いので掲載), date-fns, Day.js, luxon あたりは以下のようになります。
// moment
moment('2022-02-03 04:05:06').format(); // 2022-02-03T04:05:06+09:00
// date-fns
import formatISO from 'date-fns/formatISO';
formatISO(new Date('2022-02-03 04:05:06'));// 2022-02-03T04:05:06+09:00
// Day.js
import * as dayjs from 'dayjs'
dayjs('2022-02-03 04:05:06').format(); // 2022-02-03T04:05:06+09:00
// luxon
import { DateTime } from "luxon";
new DateTime("2022-02-03 04:05:06").toISO(); // 2022-02-03T04:05:06+09:00
DateTime.local()
二つ目は丁寧に素の Date オブジェクトから手で文字列を構築する方法です。例えばこれは次の様にできます。
/**
* システムロケールのISO形式文字列を返す
* @param {Date|string} date 変換元日時
* @returns {string} ISO形式文字列
*/
function toLocaleIsoString(date) {
// 以降の処理で必ず Date 型を取り扱うために変換
if (!(date instanceof Date)) {
date = new Date(date)
}
// もし不正な日時ならば'Invalid Date'を返す
// 'Invalid Date'は Date オブジェクトの仕様で定められている日付として不正な時に返す文字列
// @see https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date-objects
if (date.toString() === 'Invalid Date') {
return 'Invalid Date';
}
// 連続して同じ処理を行うので関数化して略しやすくする
// 1桁の数値でも必ず2桁になるようにする
const pad = num => String(num).padStart(2, '0');
// Date オブジェクトの各メソッドと↑のパディング関数を用いてISO形式を構築するための年月日時分秒の文字列を用意
const yyyy = date.getFullYear();
const MM = pad(date.getMonth() + 1);
const dd = pad(date.getDate());
const hh = pad(date.getHours());
const mm = pad(date.getMinutes());
const ss = pad(date.getSeconds());
// タイムゾーン文字列を用意
// getTimezoneOffset メソッドは現在のロケールから協定世界時 (UTC) までのタイムゾーンの差を分単位で返すのでマイナスをかけて反転
const tzMin = -date.getTimezoneOffset();
// 分のみで構成されたタイムゾーンから符号、時、余りの分を抜き出してISO形式を構築するためのタイムゾーン文字列を用意
const timezone = `${tzMin >= 0 ? '+' : '-'}${pad(Math.floor(Math.abs(tzMin) / 60))}:${pad(Math.abs(tzMin) % 60)}`
// ここまでで用意したそれぞれをISO形式になるようにとりまとめる
return `${yyyy}-${MM}-${dd}T${hh}:${mm}:${ss}${timezone}`;
}
console.log(toLocaleIsoString(new Date('2022-02-03 01:02:03')));// 2022-02-03T01:02:03+09:00
console.log(toLocaleIsoString('sdsadasad'));// Invalid Date
ライブラリを使えない、使わない方が良いような環境でも安心して使えるのがメリットです。ライブラリが使えるならば出番はないですが、どこかしらから引き出せると役に立つときもあります。
三つ目はワンライナーでやる方法です。toLocaleString で日時文字列を出力し、それを元に文字列変換します。これは次の様にできます。
new Date('2022-02-03 01:02:03')
.toLocaleString('sv', {timeZoneName: 'longOffset'})// 2022-02-03 01:02:03 GMT+09:00から
.replace(' ', 'T').replace(' GMT', '')// ISOになるように各部を入れ替えるとISO形式になる
実装方法は様々ですが、例の様にタイムゾーンを考慮した ISO 形式文字列を出力できます。システムロケール、UTC以外のロケールのタイムゾーンを使う場合はまた別途実装が必要ですが(luxonはタイムゾーン周りの処理が得意らしいです)、このあたりを使えば大方対応できるはずです。