系统时间
在操作系统中,时间(时钟)由三个部分决定:时间值、它是本地时间还是 UTC 或其他时间、时区,以及适用的夏令时 (DST)。本文解释了它们是什么以及如何读取/设置它们。系统中存在两个时钟:硬件时钟和系统时钟,本文也对此进行了详细介绍。
大多数操作系统的标准行为是
- 在启动时从硬件时钟设置系统时钟。
- 保持系统时钟的准确性,参见 #时间同步。
- 在关机时从系统时钟设置硬件时钟。
时间标准
有两种时间标准:本地时间 (localtime) 和 协调世界时 (UTC)。本地时间标准取决于当前的时区,而 UTC 是全球时间标准,与时区值无关。尽管概念上有所不同,UTC 也被称为 GMT (格林威治标准时间)。
硬件时钟 (CMOS 时钟,BIOS 时间) 使用的标准由操作系统设置。默认情况下,Windows 使用本地时间,macOS 使用 UTC,其他 UNIX 和类 UNIX 系统则各不相同。使用 UTC 标准的操作系统通常会认为硬件时钟是 UTC,并在启动时根据时区调整它来设置操作系统时间。
硬件时钟
硬件时钟 (也称为实时时钟 (RTC) 或 CMOS 时钟) 存储以下值:年、月、日、时、分和秒。一个 UEFI 固件还具有存储时区以及是否使用 DST 的附加功能。
读取硬件时钟
# 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 年),那么在启动后的前 11 分钟内,任何需要相对准确时间的功能都会出错,包括 SSL,它使用 在线证书状态协议 (OCSP)。您计算机上运行的 Web 浏览器通常会在其对网站的请求中发送硬件时钟时间,如果时间偏差太大,浏览器将因 OCSP 错误而拒绝连接。
系统时钟
系统时钟 (也称为软件时钟) 记录:时间、时区以及适用的 DST。它由 Linux 内核计算为自 1970 年 1 月 1 日午夜 UTC 以来的秒数。系统时钟的初始值根据 /etc/adjtime 的内容从硬件时钟计算得出。引导完成后,系统时钟独立于硬件时钟运行。Linux 内核通过计算计时器中断来跟踪系统时钟。
读取时钟
要检查当前的系统时钟时间 (同时以本地时间和 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 以避免跨系统冲突。否则,如果硬件时钟设置为本地时间,则可能一个以上的操作系统会在 DST 更改后进行调整,从而导致过度纠正;在跨时区旅行时,使用其中一个操作系统来重置系统/硬件时钟也可能出现问题。
可以使用 timedatectl 命令查询和设置硬件时钟。您可以使用以下命令查看 Arch 系统的当前硬件时钟时间标准
$ timedatectl | grep local
RTC in local TZ: no
要将硬件时钟时间标准更改为本地时间,请使用
# 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 环境下 (例如安装过程中) 可能无法使用此命令。在这些情况下,您可以恢复使用 hwclock 命令,或者使用 systemd-nspawn 而不是 chroot。 - 如果
/etc/adjtime文件不存在,systemd 会假定硬件时钟设置为 UTC。
Microsoft Windows 中的 UTC
要与 Windows 双启动,建议将 Windows 配置为使用 UTC,而不是将 Linux 配置为使用本地时间。(Windows 默认使用本地时间 [2])。
可以通过简单的注册表修复来完成:打开 regedit,然后在注册表项 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation\RealTimeIsUniversal 下添加一个值为十六进制 1 的 DWORD 值。
您可以从以管理员身份运行的命令提示符中执行此操作
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 请求因 DST 更改而更新时钟,请允许它。这将使时钟保持 UTC 状态,仅更正显示的时间。
如果您在时间偏移方面遇到问题,请尝试重新安装 tzdata,然后重新设置您的时区。
# timedatectl set-timezone America/Los_Angeles
Ubuntu/Fedora 中的 UTC
许多 Linux 发行版会在安装过程中检测到任何磁盘上有 Windows 时,将硬件时钟设置为被解释为“本地时间”。这显然是故意为之,以便让新用户在不编辑注册表的情况下在 Windows 计算机上尝试 Linux。
有关更改此行为,请参见上文。
多 NTP 交互
如果您使用 NTP 客户端 (参见下文的 #时间同步) 来跟踪任何系统上的 RTC 漂移,您应该只在一个系统上禁用时间同步。否则,NTP 客户端将不知道彼此的调整,并对 RTC 漂移做出严重不正确的估计。
对于 Windows,请转到日期和时间设置,然后取消勾选时间同步选项。您也可以以管理员身份运行 w32tm /unregister 来注销时间同步服务:Active Directory 机器已知 会忽略同步设置,并仍然执行同步以防止重放攻击。Windows 时钟同步例程本身就相当不准确,需要额外的工作才能达到一秒的精度,因此禁用它可能不会损失太多。
时区
要检查当前为系统定义的时区
$ timedatectl status
要列出可用时区
$ timedatectl list-timezones
要设置您的时区
# timedatectl set-timezone Area/Location
其中 Area 是大陆或海洋,Location 是区域内的具体位置。北美洲和南美洲共享同一个区域—America。[3]
示例
# timedatectl set-timezone America/Toronto
这将创建一个指向 /usr/share/zoneinfo/ 下的 zoneinfo 文件的 /etc/localtime 符号链接。如果您选择手动创建链接 (例如在 timedatectl 无法工作的 chroot 环境中),请记住它必须是符号链接,如 localtime(5) § DESCRIPTION 中所述。
# ln -sf /usr/share/zoneinfo/Area/Location /etc/localtime
有关详细信息,请参阅 timedatectl(1) 和 localtime(5)。
基于地理位置的设置
为了根据 IP 地址位置自动设置时区,可以使用地理定位 API 来检索时区,例如 curl https://ipapi.co/timezone,并将输出传递给 timedatectl set-timezone 进行自动设置。以下是一些提供免费或部分免费服务的地理 IP API
每次 NetworkManager 连接到网络时更新时区
时间偏差
每个时钟的值都与真实时间 (其中 国际原子时 是最佳表示) 有所不同;没有时钟是完美的。基于石英的电子时钟走时不准确,但保持一致的不准确性。这种基本的“不准确性”被称为“时间偏差”或“时间漂移”。
当使用 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 — 该协议的参考实现。
- ntpd-rs — 一个具有 NTS 支持的完整功能 NTP 实现。
- NTPsec — NTPd 的一个分支,专注于安全性。工作方式类似,但删除了大量旧代码。
- https://ntpsec.org/ || ntpsecAUR
简单网络时间协议 (SNTP)
任何功能少于标准 NTP 节点的东西都被认为是 简单网络时间协议 (SNTP)。一个基本的 SNTP 客户端可能只是从单个服务器获取时间并立即设置,而不跟踪长期漂移。SNTP 提供较低的精度,但占用资源更少。其精度通常足以满足桌面用户和嵌入式工作负载,但对 NTP 服务器来说是不可接受的。以下是 SNTP 的实现:
- ConnMan — 一个轻量级的网络管理器,支持 SNTP。
- ntpclient — 一个简单的命令行 SNTP 客户端。
- OpenNTPD — OpenBSD 项目的一部分,实现了 SNTP 客户端和服务器。不支持闰秒。
- sntp — 一个随 NTPd 附带的 SNTP 客户端。它取代了 ntpdate,并且在非服务器环境中使用是推荐的。
- systemd-timesyncd — 一个简单的 SNTP 守护进程,仅实现客户端功能,专注于从一个远程服务器查询时间。对于大多数安装来说,它应该足够了。
每个用户/会话或临时设置
对于某些用例,更改时间设置而不触及全局系统值可能很有用。例如,在开发过程中测试依赖于时间的应用程序,或者在从另一个时区远程登录服务器时调整系统时区。
要使应用程序“看到”与系统不同的日期/时间,您可以使用 faketime(1) 工具 (来自 libfaketime)。
如果您希望应用程序“看到”与系统不同的时区,请设置 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 时区的系统上开发时 DST 更改的影响。
另一个用例是为同一系统的不同用户设置不同的时区:这可以通过在 shell 的配置文件中设置 TZ 变量来实现,请参见 环境变量#定义变量。
技巧与提示
fake-hwclock
alarm-fake-hwclock 专为没有电池支持的 RTC 的系统设计,它包含一个 systemd 服务,该服务在关机时保存当前时间,在启动时恢复保存的时间,从而避免了奇怪的时间旅行错误。
安装 fake-hwclock-gitAUR,然后 启动/启用 服务 fake-hwclock.service。
虚拟 PTP
虚拟机来宾可以通过 PTP (Precision Time Protocol) /dev/ptp0 接口从主机获取时间。与在主机和来宾之间使用 IP 上的 NTP 相比,此接口更精确。
- 在 KVM 机器上,需要加载
ptp_kvm内核模块以提供虚拟 PTP 设备。请参见 VM 时间管理:在 KVM 上使用 PTP 硬件时钟。 - 在 Hyper-V 机器上,来宾集成应该会生成一个
/dev/ptp0,而无需额外配置。
chrony 和 ntpd 都可以使用虚拟-PTP 设备在来宾和主机之间同步时间,方法是将该设备配置为真实的 PTP 参考时钟。
故障排除
时钟显示的值既不是 UTC 也不是本地时间
这可能是由多种原因引起的。例如,如果您的硬件时钟运行在本地时间,但 timedatectl 被设置为假定它在 UTC,那么结果将是您的时区到 UTC 的偏移量被应用两次,导致您的本地时间和 UTC 显示错误。
为了将您的时钟强制设置为正确的时间,并同时将正确的 UTC 写入您的硬件时钟,请执行以下步骤:
- 设置 ntpd (无需将其启用为服务)。
- 正确设置您的时区。
- 运行
ntpd -qg以手动将您的时钟与网络同步,忽略本地 UTC 和网络 UTC 之间的较大偏差。 - 运行
hwclock --systohc将当前软件 UTC 时间写入硬件时钟。