システム時刻
オペレーティングシステムでは、時間(時計)は4つのパーツで決まっています: 時刻値・時刻系・標準時間帯・夏時間 (Daylight Saving Time, DST)。この記事ではこれらが何なのか、そしてどうやって読み込み・設定するのか説明します。ネットワークを使って正確なシステム時刻を維持する方法は Network Time Protocol を見て下さい。
目次
ハードウェアクロックとシステムクロック
コンピューターは2つの時計を持っていることを考慮しなくてはなりません: "ハードウェアクロック" と "システム(ソフトウェア)クロック" です。
ハードウェアクロック (又の名をリアルタイムクロック (Real Time Clock, RTC) もしくは CMOS クロック) は次の値を保存しています: 年・月・日・時・分・秒。時刻系 (localtime か UTC) や夏時間が使われているかどうかの情報を保存することはできません。
システムクロック (又の名をソフトウェアクロック) は次の情報を記録しています: 時刻、タイムゾーン、そして 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) だと認識して、あなたのタイムゾーンによってその時間を調整してシステム時刻を設定します。
Linux を使う時はハードウェアクロックを UTC 時刻系に設定するのが有益です。Linux でハードウェアクロックを UTC で定義すれば夏時間が自動的に使われます。地方時系を使う場合、システム時刻は DST によって変わらなくなり他のオペレーティングシステムが DST の切り替えを引き受けることになります (そして NTP エージェントは動作しません)。
ハードウェアクロックの時刻系の設定はコマンドラインから可能です。Arch Linux をインストールした時にあなたがどちらを設定したのか確認するには:
$ timedatectl status | grep local
ハードウェアクロックは timedatectl
コマンドで尋ねたり設定できます。ハードウェアクロックの時刻系を localtime に変更するには次を実行してください:
# timedatectl set-local-rtc true
逆に時刻系を UTC に設定するには次を実行してください:
# timedatectl set-local-rtc false
ハードウェアクロック (RTC) を localtime に設定すると、夏時間の扱いで問題が生じるので注意してください。コンピュータの電源が切られている時に夏時間が変わると、次の起動時に時計がおかしくなります (詳しくは ここ を見て下さい)。最近のカーネルでは起動時にシステム時刻を直接 RTC からセットしますが、その際カーネルは RTC を UTC としてみなします。RTC が localtime だった場合、起動時に毎回システム時刻は間違って設定され、その後修正されることになります。これは予期せぬバグを引き起こす温床になりかねません (時計の針が戻るのは良いことばかりではないのです)。
上のコマンドで /etc/adjtime
が自動的に設定されます; 他の設定は必要ありません。
カーネルの起動中、RTC ドライバーがロードされた時点で、ハードウェアクロックからシステムクロックを設定することができます。設定するかどうかはハードウェアプラットフォーム、カーネルのバージョン・ビルドオプション次第です。設定される場合、ブートシーケンスで既に、ハードウェアクロックは UTC と認識され /sys/class/rtc/rtcN/hctosys
(N=0,1,2,..) の値は 1 に設定されます。その後、/etc/adjtime
の値を使って、再度 systemd によってハードウェアクロックからシステムクロックが設定されます。そのため、ハードウェアクロックで localtime を使っているとブートシーケンス中に予期しない動作が発生するかもしれません。例えば、システム時刻が後戻りするなど。従ってこの設定は良いアイデアとは言えません。
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) を見てください。
クロックスキュー
全ての時計は本当の時間 (一番正確なのは国際原子時) とは違う値を示しています、どの時計も完全ではありません。クォーツを使っている電子時計は(不完全な)時間を刻んでいますが、規則的なズレを生じさせています。この本質的な'ズレ'は'クロックスキュー'や'クロックドリフト'として知られています。
ハードウェアクロックが hwclock
で設定された時、新しいドリフト値(一日に何秒ズレるか)が算定されます。このドリフト値は新しく設定された値と以前に設定されていたハードウェアクロックの値の差から計算され、以前のドリフト値とハードウェアクロックが最後に設定された時間を考慮しています。新しいドリフト値と時計が設定された時刻は /etc/adjtime
ファイルに書き込まれ以前の値を上書きします。これにより hwclock --adjust
を実行することでハードウェアクロックのずれを調整できるようになっています。また、hwclock
デーモンが有効になっている場合シャットダウン時にも実行されます (よって systemd を使っているシステムでは実行されません)。
ハードウェアクロックが時間の消失と取得を何度も繰り返している場合、不正なドリフトが保存されている可能性があります (ただし hwclock デーモンが実行中の場合に限ります)。これはハードウェアクロックの時間を間違って設定したり時刻系を Windows や Mac OS と合わせていないときに起こります。/etc/adjtime
を削除してドリフト値を削除してから、正しいハードウェアとシステムクロックの時間を設定して、時刻系が直ったか確認してください。
ソフトウェアクロックはかなり正確ですが、他の時計と同じく完全に正しいわけではありませんし同じようにズレが起こります。稀に、カーネルが割り込みを飛ばしたときにシステム時刻が正確さを失うことがあります。ソフトウェアクロックの正確性を改善するツールが複数存在します。#時刻同期を見てください。
時刻同期
Network Time Protocol (NTP) はパケット交換でデータの遅延時間が不確定なネットワークを介してコンピュータシステムの時刻を同期するためのプロトコルです。NTP の実装は以下が存在します:
- Network Time Protocol daemon — プロトコルのリファレンス実装であり、特に時刻サーバーでの使用が推奨されます。NTPd は割り込みの周期や1秒辺りのティックの数を調整してシステムクロックのずれを減らすことも可能です。NTPd の実行中は11分ごとにハードウェアクロックの再同期も行われます。
- sntp — NTPd に付属している SNTP クライアント。ntpdate を置き換えることができ、サーバー以外の環境での使用が推奨されます。
- systemd-timesyncd — クライアント側だけを実装したシンプルな SNTP デーモン。リモートサーバーに時刻を問い合わせることだけを行います。大抵の環境ではクライアントだけで十分です。
- OpenNTPD — クライアントとサーバーの両方を実装する OpenBSD プロジェクト。
- Chrony — ノートパソコンなど常にオンラインとは限らない環境用に設計されたクライアント・サーバー。
- ntpclient — シンプルなコマンドライン NTP クライアント。
ヒントとテクニック
fake-hwclock
alarm-fake-hwclock は RTC をバックアップするバッテリーが搭載されていないシステム用のスクリプトで、シャットダウン時に現在時刻を保存して、起動時に保存した時刻を復元する systemd サービスが付属しています。これによって時刻が飛んでしまうエラーを防ぐことができます。
fake-hwclock-gitAUR をインストールして fake-hwclock.service
を起動・有効化してください。
ユーザー別あるいは一時的に時刻を設定
ときとしてシステムの時刻は変えずに一時的に設定を変えたいということがあるでしょう。例えば、時間を使用するアプリケーションを開発していて実際に動きをテストしたい場合やタイムゾーンが異なる場所からサーバーにログインしている場合などです。
アプリケーションにシステム時刻以外の時刻を使わせたいときは (libfaketime に含まれている) faketime あるいは datefudge ユーティリティが使えます。
アプリケーションにシステム設定のタイムゾーン以外のタイムゾーンを使わせたいときは、以下のように 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
変数を設定することでユーザーごとにタイムゾーンを設定できます。環境変数#変数の定義や自動起動#シェルを参照してください。
トラブルシューティング
時計が示している値が UTC でも localtime でもない
さまざまな理由が考えられます。例えば、ハードウェアクロックが localtime で動いているのに、timedatectl
が UTC に設定されていると、タイムゾーンのオフセットが UTC に二重に適用されることで、localtime でも UTC でもない間違った値になってしまいます。
時計を正しい時刻に直して、ハードウェアクロックに正しい UTC を書き込むには、以下の手順に従って下さい:
- NTP を設定してください (サービスとして有効にする必要はありません)。
- タイムゾーンを適切に設定してください。
ntpd -qg
を実行して手動で時計をネットワークと同期してください、ローカル UTC とネットワーク UTC の時差は無視します。hwclock --systohc
を実行して現在のソフトウェア UTC 時刻をハードウェアクロックに書き込んで下さい。