UNIX系OSでの日付表記をISO 8601 (YYYY-MM-DD) にする方法
概要
UNIX系OSでの日付表記をISO 8601にする方法を調べた。結論としては,以下のコードを~/.profileか~/.bash_profileに記して,ログインシェルの設定に反映させればよい。
## Use ISO 8601 date time format (YYYY-MM-DDThh:mm:ss).
ISO8601_LOCALE=$(command -v locale >/dev/null && locale -a | sort -r |
grep -e en_CA.UTF-8 -e en_DK -e se_NO -e si_LK -e sv_SE.ISO | head -n 1)
LC_TIME=$ISO8601_LOCALE date +%x | grep -q - && export LC_TIME=$ISO8601_LOCALE
導入
ノートパソコンにLinuxベースのOSであるUbuntuを使っている。トラブルがあったときに調べやすいように,言語設定 (ロケール) を英語にして,GUIやメッセージなどを英語表記にしている。
概ね満足しているのだが,一点気に入らないことがある。それは,日付の表示形式だ。
単にロケールを英語にすると,MM/DD/YY
の形式になってしまう。これは以下のコマンドでも確認できる。
LC_ALL=C date +%x # 現在日時をロケールに従って表示
09/22/18
主にアメリカで使われるこの日付表記は,イギリスでの日時表記 (DD/MM/YY
) と日にちと月の位置が入れ替わっており,非常に紛らわしい。例えば,2012-09-11が11/09/12と表示された場合,何年何月何日を指すのか特定できず,トラブルの原因となる。
この問題を避けるために,ISO 8601の日付表記を採用したい。ISO 8601はYYYY-MM-DD
の形式で日付を表記する。この形式であれば,世界中のどの国の人でも何年何月何日かが一目で判断可能だ。
そこで,Linuxのロケールに日付表記がISO 8601を指定したい。LinuxやMacOS,Android,FreeBSDなどのUNIX系OSでは,LC_TIME環境変数が日時表記を制御している。そのため,LC_TIMEにISO 8601が適用されるロケールを指定する。
具体的に何の値を指定すれば,よいか調べてみたのだが,はっきりとした根拠のあるものが見つからず,OSの設定を確認するのが確実だと判断した。そこで,主要なOSでISO 8601の時刻表記に適用可能なロケールを調査した。
Linux
Linuxでは,/usr/share/i18n/locales/配下にlocaledef
で生成するロケールファイルの入力ファイルが格納されている模様。ここのファイル群で書かれているロケール定義はPOSIXで定義されている。
特に,日付の書式は以下のd_fmtで定義されている。
具体的には,以下のように記述されている。
Unicodeの文字コードで記されているのでぱっとみわかりにくいが,これは%Y-%m-%d
に等しい。
従って,Linuxの場合/usr/share/i18n/locales/配下のロケールと同名のファイル内に,上記の記述があるものが,日付表記にISO 8601を採用しているロケールだ。
このロケールを見つけるには,例えば以下のコードを実行すればよい。
ただし,この方法はロケールの定義が用意されているLinuxだけで通用する。MacやFreeBSDなどのBSD系のOSは別の方法でロケールを定義しているようで,Linuxでしか使えない。
Ubuntu
手始めに,自分が使っているUbuntu 16.04のISO 8601対応ロケールを調べた。先ほどのコードをUbuntu 16.04で実行すると以下が出力された。
en_DK.utf8 en_CA.utf8
実際に,以下のコマンドで反映されることを確認できる。
LC_TIME=en_DK.utf8 date +%x
LC_TIME=en_CA.utf8 date +%x
2018-09-22
2018-09-22
このため,Ubuntu 16.04ではLC_TIMEにen_DK.utf8かen_CA.utf8を指定すれば,日付表記をISO 8601にできることがわかった。
Mac
続いて,Mac (OS X Snow Leopard 10.3.0) でのロケールには何を指定したらよいかを調査した。
MacやFreeBSDなどのBSD系OSでは,前述の方法は使えない。そこで,ロケールに関するファイルの格納ディレクトリーから,%Y-
のキーワードで検索する。具体的には,以下のコマンドで検索した。シンボリックリンクが存在したので,-Lでリンクを辿らないようにしている。
find -L /usr/share/locale/ -type f -exec grep -i %y- {} +
/usr/share/locale/sv_SE.ISO8859-1/LC_TIME:%Y-%m-%d
/usr/share/locale/sv_SE.ISO8859-15/LC_TIME:%Y-%m-%d
その結果,sv_SE.ISO8859-1とsv_SE.ISO8859-15がヒットした。
実際にLC_TIMEにこの値を指定して実行したところ,きちんとISO 8601の日付表記が反映された。
LC_TIME=sv_SE.ISO8859-1 date +%x
LC_TIME=sv_SE.ISO8859-15 date +%x
2018-09-22
2018-09-22
また,Macのベースとなっている,FreeBSD (v9)で同じようにロケールを検索した。
find -L /usr/share/locale/ -type f -exec grep -i "%y-" {} +
/usr/share/locale/sv_SE.ISO8859-1/LC_TIME:%Y-%m-%d /usr/share/locale/sv_SE.ISO8859-15/LC_TIME:%Y-%m-%d /usr/share/locale/sv_SE.UTF-8/LC_TIME:%Y-%m-%d
こちらは,sv_SE.ISO8859-1とsv_SE.ISO8859-15だけでなく,sv_SE.UTF-8も存在していた。そして,これらの3種類のロケールで,LC_TIMEが反映されることがわかった。
調査
UbunutuとMacでの調査から,OSごとにISO 8601の反映に使用するロケール値が異なることがわかった。そこで,もう少し数を広げて調査することにした。具体的には,商用UNIX (Solaris, UnixWare, HP-UX),主要なLinuxディストリビューションのCentOS,組み込み系OS (OpenWrt),Windows (Cygwin/MSYS2, WSL) だ。
具体的には,OSごとに以下の内容のシェルコマンドを実行して日付の形式を目視で判断した。
調査結果を以下の表にまとめた。
OS | ロケール検索場所 | ISO 8601ロケール | コメント |
---|---|---|---|
Ubuntu 16.04 | /usr/share/i18n/locales | en_DK.utf8 en_CA.utf8 | |
CentOS 5 | /usr/share/i18n/locales | csb_PL, csb_PL.utf8 de_AT, de_AT@euro, de_AT.iso88591, de_AT.iso885915@euro, de_AT.utf8 de_BE, de_BE@euro, de_BE.iso88591, de_BE.iso885915@euro, de_BE.utf8 de_CH, de_CH.iso88591, de_CH.utf8 de_LU, de_LU@euro, de_LU.iso88591, de_LU.iso885915@euro, de_LU.utf8 en_DK, en_DK.iso88591 , en_DK.utf8 fr_CA, fr_CA.iso88591, fr_CA.utf8 hu_HU, hu_HU.iso88592, hu_HU.utf8 pl_PL, pl_PL.iso88592, pl_PL.utf8 se_NO, se_NO.utf8 si_LK, si_LK.utf8 sv_FI, sv_FI@euro, sv_FI.iso88591, sv_FI.iso885915@euro, sv_FI.utf8 sv_SE, sv_SE.iso88591, sv_SE.iso885915, sv_SE.utf8 | ro_RO*は違う形式。 |
Mac OS 10.3.0 | /usr/share/locale/ | sv_SE.ISO8859-1 sv_SE.ISO8859-15 | sv_SE, sv_SE.UTF-8とその他,de_AT*, de_CH*, en_CA* (ISO8859-1, ISO8859-15, US-ASCII, UTF-8), fr_CA*, hu_HU*, pl_PL*, ro_RO*は違う形式。 |
FreeBSD | /usr/share/locale/ | sv_SE.ISO8859-1 sv_SE.ISO8859-15 sv_SE.UTF-8 | de_AT*, de_CH*, en_CA*, fr_CA*, hu_HU*, pl_PL*, ro_RO*は違う形式。 |
Solaris 10 | /usr/share/locale/ | 商用UNIX。locale -a の出力がC,POSIX,iso_8859_1しかなく,ISO 8601利用不可能。 | |
UnixWare 7.1.1 | /usr/lib/locales | en_CA.UTF-8, fr_CA, fr_CA.850, fr_CA.863 ro_RO.ISO8858-2, ro_RO.UTF-8 sv sv_FI, sv_FI.437, sv_FI.850, sv_FI.UTF-8 sv_SE.437, sv_SE.850, sv_SE.UTF-8 | 商用UNIX。en_CA.UTF-8のみYYYY-MM-DDだが,その他はYY-MM-DDと年が2桁になる。 de_AT*, de_CH,*, en_CA* (UTF-8以外), hu_HU*, pl_PL*は違う形式。 |
HP-UX 11.31 | /usr/lib/nls/loc /src/ | /usr/lib/nls/loc/srcにロケール定義ファイルが存在するが,ISO 8601形式の日付定義なし。 fr_CA*, hu_HU*, pl_PL, ro_RO*, sv_SE* (sv_SE.iso88591, sv_SE.iso885915@euro, sv_SE.roman8, sv_SE.utf8) は違う形式。 | |
OpenWrt 15.05 | 組込み機器向けOS。localeコマンドが存在せず,ロケール自体が不在。 | ||
Cygwin/MSYS2 | /usr/share/locale/ | en_CA, en_CA.utf8 fr_CA, fr_CA.utf8 se_NO, se_NO.utf8 si_LK, si_LK.utf8 sv_FI, sv_FI.utf8, sv_FI@euro sv_SE, sv_SE.utf8 | /usr/share/locale/にはバイナリーしか存在しないため,他のOSで実績のあるロケールを試して確認。 ro_ROは違う形式。 |
Windows Subsystem for Linux (Windows 10,バージョン1607,OSビルド14393.2189) | /usr/share/locale/ | locale -a の出力がC, C.UTF-8, POSIX, en_US.utf8, ja_JP.utf8しかない。 |
この表から以下のことがわかった。
1. に記した通り,ロケールファイル内には記述がないにも関わらず,ISO 8601であるロケールがいくつかあった。しかし,2. に記した通り,これらの値はOSごとにISO 8601の場合とそうでない場合があるので注意が必要だ。とくに,3. に記した通り,Mac OS 10.3.0でsv_SEとsv_SE.UTF-8がダメなのが解せない。また,4に記した通り,HP-UXではMacとFreeBSDで唯一ISO 8601に対応しているsv_SEが違う形式となっている。幸いなことに,HP-UXのsv_SEはsv_SE.isoと小文字のisoなので,大文字のISOで識別可能だ。5. に記したUnixWareでは,en_CA.UTF-8のみISO 8601に対応している。en_CAはISO 8601に対応していないOSがそんざいするので注意を要する。そして,6. に記したのいずれかのロケールが存在する場合は,その値を使えばISO 8601を実現できそうだ。
方法
この調査結果を元に,ISO 8601をロケールに指定可能な場合に,指定するコードは以下となる。
実装方針は以下の通りだ。
Solaris 10やOpenWrt,WSLのように,ISO 8601対応ロケールがそもそも存在しないOSがあったので,その場合はデフォルト値を変更しないようにした。
あとは,上記のコードをOS起動時のログインシェルの設定ファイルである~/.profile
や~/.bash_profile
に記入して,ログイン時に読み込むようにすれば,ログイン後に反映される。
LC_TIMEはOS全体に影響があるので,ThunderbirdやWebブラウザーなど,LC_TIMEを参照している箇所全てに反映される。アプリケーションごとに個別に表示形式を指定する必要がなくなる。
結論
きっかけは,メールクライアントのThunderbirdの日時表記だった。毎日使うので気になっていた。
最初調べたときは,Ubuntuのen_DKとMacのsv_SEだけで対応できると思っていた。しかし,念の為他の環境を調べると,商用UNIXのUnixWareとHP-UXで厄介な競合があり,またMac OSもsv_SE単独とsv_SE.UTF-8の2個だけだがISO 8601に対応していないなど,頭を悩ました。
調査に際し,POSIXのLC_TIMEの定義を確認するなど,思っていた以上に複雑で時間がかかってしまった。しかし,その分確実な内容にできたのでよかった。これで,今後はアプリケーションの日付形式で悩むこともないだろう。