系統時間
在作業系統中,時間(時鐘)取決於三個部分:時間值(本地時間或 UTC 等)、時區及夏令時(DST,需要時應用,在中國已廢止)。本文將分別介紹各個部分的定義及如何設置和讀取它們。系統中也存在兩種時鐘:硬體時鐘和系統時鐘,下文也將進行詳細描述。
大部分作業系統的時間管理包括如下方面:
- 啟動時根據硬體時鐘設置系統時間
- 保持系統時鐘準確,細節請參考#時鐘同步
- 關機時根據系統時鐘設置硬體時鐘
時間標準[編輯 | 編輯原始碼]
時間表示有兩個標準:localtime 和 UTC(Coordinated Universal Time)。localtime 標準依賴於當前時區,而 UTC 是與時區無關的全球時間標準。儘管概念上有差別,UTC 和 GMT(格林威治時間)是一樣的。
硬體(CMOS 時鐘,即 BIOS 時鐘)所使用的時間標準由作業系統設定,Windows 默認使用 localtime,macOS 默認使用 UTC ,而其他的 UNIX 和類 UNIX 作業系統各不相同。使用 UTC 標準的作業系統通常會認為硬體時鐘也是 UTC,並在啟動時根據時區調整到對應時間。
硬體時鐘[編輯 | 編輯原始碼]
硬體時鐘(即實時時鐘 RTC 或 CMOS 時鐘)僅能保存:年、月、日、時、分、秒這些時間數值。UEFI 固件可以額外存儲時區以及是否使用夏令時的信息。
讀取硬體時鐘[編輯 | 編輯原始碼]
# hwclock --show
根據系統時鐘設置硬體時鐘[編輯 | 編輯原始碼]
下面命令將系統時鐘設置到硬體時鐘,同時會創建或更新 /etc/adjtime
。詳情請參考 hwclock(8) § The Adjtime File 和後面的 #時鐘偏移段落。
# hwclock --systohc
自動同步[編輯 | 編輯原始碼]
默認配置下,Arch Linux 內核會每 11 分鐘同步一次硬體時鐘和系統時鐘。你可以使用以下命令查看你的內核中是否已啟用該功能:
$ zgrep CMOS /proc/config.gz
CONFIG_GENERIC_CMOS_UPDATE=y CONFIG_RTC_DRV_CMOS=y
首次同步發生在啟動階段,也就是說如果硬體時鐘誤差較大(例如 CMOS 電池失效導致時鐘被重置到 2000 年),那任何需要準確時間的操作都將報錯(包括 SSL 和在線證書狀態協議(OCSP))。瀏覽器通常會將硬體時鐘附在發往網站的請求中,因此如果時鐘誤差過大,就會因 OCSP 錯誤導致瀏覽器拒絕進行連接。
系統時鐘[編輯 | 編輯原始碼]
系統時鐘(即軟體時間)保存了:時間、時區和夏令時設置。Linux 內核將其計算為自 UTC 時間 1970 年 1 月 1 日經過的秒數。初始系統時鐘是從硬體時間計算得來,計算時會考慮 /etc/adjtime
的設置。系統啟動之後,系統時鐘與硬體時鐘獨立運行,Linux 內核通過時鐘中斷計數維護系統時鐘。
讀取系統時間[編輯 | 編輯原始碼]
下面命令可以獲得系統時間(同時以 localtime 和 UTC 顯示)和 RTC 時間(硬體時鐘):
$ timedatectl
設置系統時鐘[編輯 | 編輯原始碼]
設置系統時間的本地時間:
# timedatectl set-time "yyyy-MM-dd hh:mm:ss"
例如:
# timedatectl set-time "2014-05-26 11:13:54"
將時間設置為 2014 年 5 月 26 日 11 時 13 分 54 秒。
多系統[編輯 | 編輯原始碼]
如果設備上安裝了多個作業系統,它們會從同一個硬體時鐘生成自己的當前時間;在這種情況下,建議將硬體時鐘設為 UTC,以避免各作業系統之間的衝突。如果硬體時鐘使用的是本地時間,那可能會有多個作業系統同時修改硬體時鐘(如進行夏令時調整),導致修正過度;另外,在跨多個時區出行的場景下,使用其中一個作業系統重置系統/硬體時鐘也可能會產生問題。
通過 timedatectl
命令可以查看並設置硬體時間。通過下面命令查看 Arch 系統當前硬體時鐘的時間標準:
$ timedatectl | grep local
RTC in local TZ: no
將硬體時間設置為 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。
後面 systemd 會根據 /etc/adjtime
重新由硬體時鐘生成系統時間。所以在硬體時鐘使用當地時間時,可能導致啟動階段出現預期外的行為,如系統時間回倒(更多信息)。所以從 systemd 216 版本開始,如果 RTC 設置為本地時間而不是 UTC,systemd 不會將時間回寫到硬體時鐘,因為這樣會引起 Windows 時間錯亂。systemd 也不會將當前時區信息傳遞給內核,這意味著 FAT 時間戳將統一按 UTC 時間處理 [1]。
- 使用
timedatectl
需要一個啟動的 D-Bus。因此,在 chroot(比如在安裝系統時)下可能無法運行 timedatectl。在這些情況下, 你可以使用 hwclock 命令或使用 systemd-nspawn 代替 chroot。 - 如果
/etc/adjtime
不存在,systemd 會假設硬體時鐘設為 UTC。
Windows 系統使用 UTC[編輯 | 編輯原始碼]
在 Arch + Windows 雙系統時,建議配置 Windows 使用 UTC 時間,而不是讓 Linux 使用 localtime。(Windows 默認使用 localtime [2])
這一步可以通過修改註冊表值達成:打開 regedit
,然後新建一個 DWORD
類型的值,位置為 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\RealTimeIsUniversal
,值為十六進制的 1
。
也可以用管理員權限啟動命令行,然後執行以下命令來完成:
C:\>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
如果 Windows 要求根據夏令時更新時鐘,可以允許。時鐘仍然是 UTC,僅是顯示時間會改變。
如果你有時間偏移問題,重新安裝 tzdata包 並再次設置你的時區:
# timedatectl set-timezone Asia/Shanghai
UTC 在 Ubuntu/Fedora 下的設置[編輯 | 編輯原始碼]
很多 Linux 發行版會在安裝時檢測計算機上是否存在 Windows,若存在則會默認使用localtime。這一操作似乎是為了讓新用戶在不修改註冊表的前提下,在 Windows 設備上試用 Linux。
要修改該行為,請參考上文。
多 NTP 交互[編輯 | 編輯原始碼]
If you use an NTP client (see #時鐘同步 below) that keeps track of RTC drift on any system, you should disable time synchronization on all but one system. Otherwise the NTP clients would be unaware of each other's adjustment and make grossly incorrect estimates of the RTC drift.
For Windows, go to the Date and time settings and uncheck the time sync option. You can also run w32tm /unregister
as an administrator to unregister the time-sync service: Active Directory machines are known to ignore the synchronization settings and perform a synchronization anyways to prevent replay attacks. The Windows clock synchronization routine is quite inaccurate to start with, requiring even extra work to reach one-second accuracy, so disabling it should not be much of a loss.
時區[編輯 | 編輯原始碼]
檢查當前時區:
$ timedatectl status
顯示可用時區:
$ timedatectl list-timezones
修改時區:
# timedatectl set-timezone <Zone>/<SubZone>
例如:
# timedatectl set-timezone Asia/Shanghai
此命令會創建一個 /etc/localtime
軟連結,指向 /usr/share/zoneinfo/
中的時區文件,如果你要手動創建連結(如在 chroot 中無法使用 timedatectl
的情況下),請確保創建的是軟連結(參閱 localtime(5) § DESCRIPTION):
手動創建(例如 chroot 中 無法執行 timedatectl
)此連結請確保是相對連結而不是絕對連結,參閱localtime(5) § DESCRIPTION。
# ln -sf /usr/share/zoneinfo/Zone/SubZone /etc/localtime
詳情請參考 timedatectl(1) 和 localtime(5)。
基於地理位置設定[編輯 | 編輯原始碼]
要根據 IP 位置自動設置時區,可以使用地理位置 API 來獲取時區信息,例如 curl https://ipapi.co/timezone
,然後將輸出傳遞給 timedatectl set-timezone
進行自動設置。有些 IP 位置 API 提供免費服務或免費額度,可參考以下清單:
在每次 NetworkManager 連接到網絡時更新時區[編輯 | 編輯原始碼]
創建一個 NetworkManager 調度腳本:
/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 的 Arch 類型系統而言不會出現該情況。
hwclock
認為時間間隔過短無法精確計算偏移,其不會重新計算偏移值。如果硬體時鐘總是大幅過快或過慢,可能是計算了錯誤的偏移值(僅適用於運行了 hwclock 守護進程的情況)。該問題會在硬體時鐘設置錯誤,或時間標準與同設備下 Windows 或 macOS 系統不一致的情況下出現。刪除文件 /etc/adjtime
可以刪除偏移值,然後重新設置正確的硬體時鐘和系統時鐘,並檢查時間標準是不是設置正確。
/etc/adjtime
中的偏移值(例如無法或不想使用 NTP 時),需要檢查調用 hwclock --adjust
命令,該操作可通過 cron 定時任務實現。軟體時鐘非常精確,但與大多數時鐘一樣,它也會出現偏移。在極少數情況下,如果內核略過了系統中斷,那系統時間就會出現偏差。有些工具可以提高軟體時鐘的精確度:
- 參考 #時鐘同步。
時鐘同步[編輯 | 編輯原始碼]
網絡時間協議(NTP)是一個通過包交換和可變延遲網絡來同步計算機系統時間的協議。
網絡時間協議(NTP)[編輯 | 編輯原始碼]
在 RFC 定義的完整 NTP 支持中,客戶端必須能夠從多個伺服器合併時間,補償延時並跟蹤系統(軟體)時鐘偏移。以下為 Arch Linux 下可用的 NTP 實現:
- Chrony — 是一個客戶端和伺服器,適合漫遊場景,是為不能始終保持在線的系統而特別設計。在多數情況下比 ntpd 收斂更快,並更接近參考值。可以跟蹤硬體時鐘(RTC)偏移。
- Network Time Protocol daemon — 是這個協議的參考實現。
- NTPsec — 專注於安全的 NTPd 分支。移除了大量舊代碼,但在使用上區別不大。
- https://ntpsec.org/ || ntpsecAUR
簡單網絡時間協議(SNTP)[編輯 | 編輯原始碼]
功能上比標準 NTP 節點少的都被認為是簡單網絡時間協議(SNTP)。最簡單的 SNTP 客戶端會從單台伺服器獲取時間,不跟蹤時鐘偏移並直接將其設置到系統上。SNTP 的準確度相對較低,但消耗的系統資源更少。其提供的準確度適用於桌面和嵌入式環境,但不適合用於 NTP 伺服器上。以下為 SNTP 的實現:
- ConnMan — 帶 SNTP 支持的輕量網絡管理器。
- ntpclient — 是簡單的命令行 NTP 客戶端。
- OpenNTPD — 是 OpenBSD 項目的一部分,同時實現了 SNTP 客戶端和伺服器。不支持閏秒。
- sntp — NTPd 附帶的 SNTP 客戶端。它取代了 ntpdate ,並被推薦用於非伺服器環境。
- systemd-timesyncd — 是一個簡單的 SNTP 守護進程。它只實現了客戶端,專用於從遠程伺服器查詢時間,更適用於絕大部分安裝的情形。
單用戶/會話或臨時設置[編輯 | 編輯原始碼]
For some use cases it may be useful to change the time settings without touching the global system values. For example to test applications relying on the time during development or adjusting the system time zone when logging into a server remotely from another zone.
To make an application "see" a different date/time than the system one, you can use the faketime(1) utility (from libfaketime包).
If instead you want an application to "see" a different time zone than the system one, set the TZ
environment variable, for example:
$ 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
This is different than just setting the time, as for example it allows to test the behavior of a program with positive or negative UTC offset values, or the effects of DST changes when developing on systems in a non-DST time zone.
Another use case is having different time zones set for different users of the same system: this can be accomplished by setting the TZ
variable in the shell's configuration file, see Environment variables#Defining variables.
提示和技巧[編輯 | 編輯原始碼]
fake-hwclock[編輯 | 編輯原始碼]
alarm-fake-hwclock designed especially for system without battery backed up RTC, it includes a systemd service which on shutdown saves the current time and on startup restores the saved time, thus avoiding strange time travel errors.
Install fake-hwclock-gitAUR, start/enable the service fake-hwclock.service
.
虛擬 PTP[編輯 | 編輯原始碼]
Virtual machine guests may obtain time from the host machine using the PTP (Precision Time Protocol) /dev/ptp0
interface. The interface is more accurate compared to using NTP over IP between the host and guest.
- On KVM machines, the
ptp_kvm
kernel module needs to be loaded to provide a virtual PTP device. See VM timekeeping: Using the PTP Hardware Clock on KVM. - On Hyper-V machines, the guest integration should spawn a
/dev/ptp0
without additional configuration.
chrony and ntpd can each use the virtual-PTP device to sync the time between guest and host, by configuring the device as if it is a real PTP reference clock.
故障排除[編輯 | 編輯原始碼]
時間顯示的既不是 UTC 也不是本地時間[編輯 | 編輯原始碼]
這可能是由多種原因造成的。假如說你的硬體時間使用的是 localtime,但 timedatectl
被設為將其用作 UTC 時間,結果是你的時區偏移設置被應用了兩次,導致系統顯示的既不是UTC也不是本地時間。
要強制校準你的時鐘,並且讓系統寫入正確 UTC 時間到硬體時鐘,遵循下面幾步:
- 設置好 ntpd(不需要將其作為服務來運行);
- 正確設置你的時區;
- 運行
ntpd -qg
以手動從網絡同步時鐘,此時在進行時間校正時,會忽略本地 UTC 時間與網絡 UTC 時間之間過大的差異; - 運行
hwclock --systohc
,將當前軟體 UTC 時間寫入硬體時間。