システム時刻
オペレーティング システムでは、時間 (クロック) は 3 つの部分によって決定されます: 時間の値、現地時間か UTC かそれ以外か、タイム ゾーン、および該当する場合は夏時間 (DST) です。この記事では、それらが何であるか、およびそれらの読み取り/設定方法について説明します。システムには、ハードウェア クロックとシステム クロックの 2 つのクロックが存在します。これについても、この記事で詳しく説明します。
ほとんどのオペレーティング システムの標準的な動作は次のとおりです。
- 起動時にハードウェア クロックからシステム クロックを設定します。
- システムクロックの正確な時刻を維持します。#時刻同期 を参照してください。
- シャットダウン時にシステム クロックからハードウェア クロックを設定します。
目次
ハードウェアクロック
ハードウェアクロック (又の名をリアルタイムクロック (Real Time Clock, RTC) もしくは CMOS クロック) は次の値を保存しています: 年・月・日・時・分・秒。2016 年以降のUEFI ファームウェアには、タイムゾーンとサマータイムを保存する機能を備えられています。
ハードウェアクロックの読み込み
# hwclock --show
システムクロックからハードウェアクロックを設定する
以下は、システムクロックからハードウェアクロックを設定するものです。さらに、/etc/adjtime
を更新するか、存在しない場合は作成します。このファイルに関するより詳しい情報は #タイムスキュー の項と同様に hwclock(8) § The Adjtime File を参照してください。
# hwclock --systohc
システムクロック
システムクロック (又の名をソフトウェアクロック) は次の情報を記録しています: 時刻、タイムゾーン、そして DST が適用されるかどうか。Linux カーネルは1970年1月1日の午前0時からの秒数でシステムクロックをはじき出します。システムクロックの初期値は /etc/adjtime
を使ってハードウェアクロックから算定されます。起動が完了した後は、システムクロックはハードウェアクロックとは独立して動作します。Linux カーネルはタイマー割り込みを数えることによりシステムクロックを記録します。
時刻を表示する
現在のシステム時刻を確認するには (UTC と現地時間のどちらも表示されます):
$ timedatectl status
root で同じコマンドを実行すればハードウェアクロックの時間も表示します。
時刻を設定する
直接システムクロックを設定するには:
# timedatectl set-time "yyyy-MM-dd hh:mm:ss"
例:
# timedatectl set-time "2014-05-26 11:13:54"
2014年5月26日、11時13分54秒に設定されます。
RTC
ほとんどのオペレーティングシステムは:
- 起動時にハードウェアクロックからシステムクロックを設定します
- NTP デーモンを使ってシステムクロックの時間を正確に保ちます
- シャットダウン時にシステムクロックからハードウェアクロックを設定します
時刻系
2つの時刻系が存在します: 地方時 (localtime) と 協定世界時 (Coordinated Universal Time, UTC) です。地方時系は現在のタイムゾーンによって決まりますが、UTC は世界時であり、タイムゾーンの値は関係ありません。概念的には異なっていますが、UTC は GMT (グリニッジ標準時, Greenwich Mean Time) とも言われます。
ハードウェアクロック (CMOS クロック, BIOS で表示される時間) で使われる時刻系はオペレーティングシステムによって定義されます。デフォルトでは、Windows は localtime を使いますが、Mac OS は UTC を使っていて、UNIX ライクなオペレーティングシステムでは決まっていません。一般的に、UTC を使う OS は起動時に CMOS (ハードウェアクロック) の時間を UTC 時間 (GMT, Greenwich time) だと認識して、あなたのタイムゾーンによってその時間を調整してシステム時刻を設定します。
複数のオペレーティングシステムがマシンにインストールされている場合、それらはすべて同じハードウェアクロックから現在の時刻を取得します。システム間の競合を避けるために、UTC に設定することをお勧めします。それ以外の場合、ハードウェアクロックが「現地時間」に設定されている場合、たとえば DST の変更後に複数のオペレーティングシステムがそれを調整する可能性があり、その結果、過剰な修正が行われます。異なるタイムゾーン間を移動し、オペレーティング システムの 1 つを使用してシステム/ハードウェア クロックをリセットするときにも、問題が発生する可能性があります。
ハードウェアクロックは、timedatectl
コマンドで照会および設定ができます。
以下を使用して、Arch システムの現在のハードウェアクロック時間標準を確認できます。
$ timedatectl | grep local
RTC in local TZ: no
ハードウェアクロックは timedatectl
コマンドで尋ねたり設定できます。ハードウェアクロックの時刻系を localtime に変更するには次を実行してください:
# timedatectl set-local-rtc true
ハードウェアクロックの時刻系を localtime に変更するには、次を実行してください:
# timedatectl set-local-rtc 1
逆にハードウェアクロックの時刻系を UTC に戻すには次を実行してください:
# timedatectl set-local-rtc 0
これらは /etc/adjtime
を自動的に生成し、それに応じて RTC を更新します。これ以上の設定は必要ありません。
カーネルの起動中、RTC ドライバーがロードされた時点で、ハードウェアクロックからシステムクロックを設定することができます。設定するかどうかはハードウェアプラットフォーム、カーネルのバージョン・ビルドオプション次第です。設定される場合、ブートシーケンスで既に、ハードウェアクロックは UTC と認識され /sys/class/rtc/rtcN/hctosys
(N=0,1,2,..) の値は 1 に設定されます。その後、/etc/adjtime
の値を使って、再度 systemd によってハードウェアクロックからシステムクロックが設定されます。そのため、ハードウェアクロックで localtime を使っているとブートシーケンス中に予期しない動作が発生するかもしれません。例えば、システム時刻が後戻りするなど。従ってこの設定は良いアイデアとは言えません。
その後、/etc/adjtime
の値に応じて、systemd によってハードウェアクロックからシステムクロックが再度設定されます。したがって、ハードウェアクロックで localtime を使用すると、ブートシーケンス中に予期しない動作が発生する可能性があります。たとえば、システム時間が逆行することは、常に悪い考えです (他にもたくさんあります)。ハードウェアクロックが UTC に設定されていてローカルタイムゾーンについてカーネルに通知しないでください。その結果、Linux システムが使用する FAT ファイルシステムのタイムスタンプは UTC になります。
Windows で UTC を使う
RTC を localtime に設定する理由に Windows とのデュアルブートをするためということがあります (Windows は localtime を使っています)。しかしながら、Windows ではレジストリを編集することで RTC で UTC を使っていても対応できるようにすることができます。従って、Linux に localtime を使わせるよりも Windows に UTC を使わせるようにすることが推奨されます。Windows に UTC を使わせる場合、Windows のインターネット時刻機能をオフにするようにしてください。オフにすると Windows はハードウェアクロックにメスを入れなくなるので、代わりに NTP デーモンを使って RTC を同期させる必要があります。
regedit
を使って、レジストリに十六進数で 1
の DWORD
値を追加してください:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\RealTimeIsUniversal
管理者権限で開いたコマンドプロンプトで以下のコマンドを実行することでも設定できます:
reg add "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TimeZoneInformation" /v RealTimeIsUniversal /d 1 /t REG_DWORD /f
もしくは、以下の内容で *.reg
ファイルを (デスクトップに) 作成しそれをダブルクリックしてレジストリにインポートしてください:
Windows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation] "RealTimeIsUniversal"=dword:00000001
DST が変わったことで Windows が時計を更新すると行ってきた場合は、それに従って下さい。Windows は時計を UTC のままにしておき、表示する時間だけ修正します。
値を設定した後にハードウェアクロックとシステムクロックの時刻を更新する必要があるかもしれません。
時計がずれる問題が発生している場合、tzdata
を再インストールしてタイムゾーンをもう一度設定してみてください:
# pacman -S tzdata # timedatectl set-timezone Asia/Tokyo
Ubuntu で UTC を使う
Ubuntu とその派生ディストリはインストール時に Windows が認識されたときにハードウェアクロックを "localtime" と認識されるように設定します。Linux 初心者が Windows コンピュータで Ubuntu を試したときにレジストリを編集しなくてもよいように故意にこのような挙動となっています。
タイムゾーン
現在のゾーンを確認するには:
$ timedatectl status
利用可能なゾーンを一覧するには:
$ timedatectl list-timezones
タイムゾーンを変更するには:
# timedatectl set-timezone <Zone>/<SubZone>
例:
# timedatectl set-timezone Asia/Tokyo
このコマンドによって /usr/share/zoneinfo/
下の zoneinfo ファイルを指し示したシンボリックリンク /etc/localtime
が作られます。リンクを手動で作成する場合 (chroot の中で timedatectl
が使用できない場合など)、archlinux(7) に書かれているように、ハードリンクではなくシンボリックリンクを使ってください:
# ln -sf /usr/share/zoneinfo/Zone/SubZone /etc/localtime
詳細は timedatectl(1), localtime(5), archlinux(7) を見てください。
地理情報を元に設定
IP アドレスの位置情報を元に自動的にタイムゾーンを設定したい場合、$ curl https://ipapi.co/timezone
などで geolocation API を使ってタイムゾーンを取得して timedatectl set-timezone
に出力を渡すことで自動設定できます。以下のサービスが無料あるいは一部無料で geo-IP API を提供しています:
- https://freegeoip.app
- https://ipapi.co/
- http://ip-api.com/
- https://ipstack.com/
- https://timezoneapi.io/
また、tzupdateAUR ツールは IP アドレスの位置情報を元にタイムゾーンを自動設定します。
NetworkManager がネットワークに接続するたびにタイムゾーンを更新する
NetworkManager dispatcher スクリプト を作成します:
/etc/NetworkManager/dispatcher.d/09-timezone
#!/bin/sh case "$2" in up) timedatectl set-timezone "$(curl --fail https://ipapi.co/timezone)" ;; esac
あるいは、ツール tzupdateAUR が IP アドレスのジオロケーションに基づいてタイムゾーンを自動的に設定します。この 最も人気のある IP 地理位置情報 API の比較 は、プロダクションでどの API を使うか決める際に役に立つかもしれません。
クロックスキュー
全ての時計は本当の時間 (一番正確なのは国際原子時) とは違う値を示しています、どの時計も完全ではありません。クォーツを使っている電子時計は(不完全な)時間を刻んでいますが、規則的なズレを生じさせています。この本質的な'ズレ'は'クロックスキュー'や'クロックドリフト'として知られています。
ハードウェアクロックが hwclock
で設定された時、新しいドリフト値(一日に何秒ズレるか)が算定されます。このドリフト値は新しく設定された値と以前に設定されていたハードウェアクロックの値の差から計算され、以前のドリフト値とハードウェアクロックが最後に設定された時間を考慮しています。新しいドリフト値と時計が設定された時刻は /etc/adjtime
ファイルに書き込まれ以前の値を上書きします。これにより hwclock --adjust
を実行することでハードウェアクロックのずれを調整できるようになっています。また、hwclock
デーモンが有効になっている場合シャットダウン時にも実行されます (よって systemd を使っているシステムでは実行されません)。
ハードウェアクロックが時間の消失と取得を何度も繰り返している場合、不正なドリフトが保存されている可能性があります (ただし hwclock デーモンが実行中の場合に限ります)。これはハードウェアクロックの時間を間違って設定したり時刻系を Windows や Mac OS と合わせていないときに起こります。/etc/adjtime
を削除してドリフト値を削除してから、正しいハードウェアとシステムクロックの時間を設定して、時刻系が直ったか確認してください。
ソフトウェアクロックはかなり正確ですが、他の時計と同じく完全に正しいわけではありませんし同じようにズレが起こります。稀に、カーネルが割り込みを飛ばしたときにシステム時刻が正確さを失うことがあります。ソフトウェアクロックの正確性を改善するツールが複数存在します。#時刻同期を見てください。
時刻同期
Network Time Protocol (NTP) はパケット交換でデータの遅延時間が不確定なネットワークを介してコンピュータシステムの時刻を同期するためのプロトコルです。NTP の実装は以下が存在します:
- Chrony — ローミングフレンドリーで、常時オンラインではないシステムのために特別に設計されたクライアントとサーバーです。ほとんどの場合、ntpd よりも速く、より参照に近い形で収束します。
- Network Time Protocol daemon — プロトコルのリファレンス実装であり、特に時刻サーバーでの使用が推奨されます。NTPd は割り込みの周期や1秒辺りのティックの数を調整してシステムクロックのずれを減らすことも可能です。NTPd の実行中は11分ごとにハードウェアクロックの再同期も行われます。
- NTPsec — NTPd のフォークで、セキュリティに重点を置いています。多くの古いコードが捨てられる以外は、同じように動作します。
- https://ntpsec.org/ || ntpsecAUR
適切な NTP ノード以下の機能を持つものは、SNTP("Sinplified" の意)とみなされます。基本的な SNTP クライアントは、長期的なドリフトを追跡することなく、単に 1 つのサーバーから時刻を取得し、すぐに設定することができます。SNTP は精度が低いですが、より少ないリソースで動作します。この精度は通常、デスクトップユーザーや組み込みワークロードには十分ですが、NTP サーバーには受け入れられません。以下は、SNTP を実装しています:
- ConnMan — A lightweight network manager with SNTP support.
- ntpclient — シンプルなコマンドライン NTP クライアント。
- OpenNTPD — クライアントとサーバーの両方を実装する OpenBSD プロジェクト。
- sntp — NTPd に付属している SNTP クライアント。ntpdate を置き換えることができ、サーバー以外の環境での使用が推奨されます。
- systemd-timesyncd — クライアント側だけを実装したシンプルな SNTP デーモン。リモートサーバーに時刻を問い合わせることだけを行います。大抵の環境ではクライアントだけで十分です。
ユーザー別あるいは一時的に時刻を設定
ときとしてシステムの時刻は変えずに一時的に設定を変えたいということがあるでしょう。例えば、時間を使用するアプリケーションを開発していて実際に動きをテストしたい場合やタイムゾーンが異なる場所からサーバーにログインしている場合などです。
アプリケーションにシステム時刻以外の時刻を使わせたいときは (libfaketime に含まれている) faketime(1) ユーティリティが使えます。
アプリケーションにシステム設定のタイムゾーン以外のタイムゾーンを使わせたいときは、以下のように TZ
環境変数を設定してください:
$ date && export TZ="/usr/share/zoneinfo/Pacific/Fiji" && date
Tue Nov 1 14:34:51 CET 2016 Wed Nov 2 01:34:51 FJT 2016
環境変数によってシステムの時刻が変わるわけではないため、プログラムの開発時に UTC からの時差を変えたり DST をテストすることが可能です。
タイムゾーンを別々に設定するケースとして同一システム上にユーザーが複数存在する場合も考えられます。シェルの設定ファイルで TZ
変数を設定することでユーザーごとにタイムゾーンを設定できます。環境変数#変数の定義や自動起動#シェルを参照してください。
ヒントとテクニック
fake-hwclock
alarm-fake-hwclock は RTC をバックアップするバッテリーが搭載されていないシステム用のスクリプトで、シャットダウン時に現在時刻を保存して、起動時に保存した時刻を復元する systemd サービスが付属しています。これによって時刻が飛んでしまうエラーを防ぐことができます。
fake-hwclock-gitAUR をインストールして fake-hwclock.service
を起動・有効化してください。
トラブルシューティング
時計が示している値が UTC でも localtime でもない
さまざまな理由が考えられます。例えば、ハードウェアクロックが localtime で動いているのに、timedatectl
が UTC に設定されていると、タイムゾーンのオフセットが UTC に二重に適用されることで、localtime でも UTC でもない間違った値になってしまいます。
時計を正しい時刻に直して、ハードウェアクロックに正しい UTC を書き込むには、以下の手順に従って下さい:
- NTP を設定してください (サービスとして有効にする必要はありません)。
- タイムゾーンを適切に設定してください。
ntpd -qg
を実行して手動で時計をネットワークと同期してください、ローカル UTC とネットワーク UTC の時差は無視します。hwclock --systohc
を実行して現在のソフトウェア UTC 時刻をハードウェアクロックに書き込んで下さい。