系统时间
在操作系统中,时间(时钟)由三个部分决定:时间值、本地时间或 UTC 或其他,时区,以及夏令时 (DST)(如果适用)。本文解释了它们是什么以及如何读取/设置它们。系统上存在两个时钟:硬件时钟和系统时钟,本文也对此进行了详细介绍。
大多数操作系统的标准行为是
- 在启动时从硬件时钟设置系统时钟。
- 保持系统时钟的准确时间,请参阅 #时间同步。
- 在关机时从系统时钟设置硬件时钟。
时间标准
有两种时间标准:本地时间和 协调世界时 (UTC)。本地时间标准取决于当前的时区,而 UTC 是全球时间标准,并且独立于时区值。尽管概念上不同,UTC 也被称为 GMT(格林威治标准时间)。
硬件时钟(CMOS 时钟,BIOS 时间)使用的标准由操作系统设置。默认情况下,Windows 使用本地时间,macOS 使用 UTC,其他 UNIX 和类 UNIX 系统各不相同。使用 UTC 标准的操作系统通常会将硬件时钟视为 UTC,并对其进行调整,以便在启动时根据时区设置操作系统时间。
硬件时钟
硬件时钟(又名实时时钟 (RTC) 或 CMOS 时钟)存储以下值:年、月、日、时、分和秒。UEFI 固件还具有存储时区以及是否使用 DST 的附加功能。
读取硬件时钟
# hwclock --show
从系统时钟设置硬件时钟
以下命令从系统时钟设置硬件时钟。此外,它还会更新 /etc/adjtime
,如果不存在则创建它。有关此文件以及 #时间偏差 部分的更多信息,请参阅 hwclock(8) § Adjtime 文件。
# 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 Zone/SubZone
示例
# timedatectl set-timezone Canada/Eastern
这将创建一个指向 /usr/share/zoneinfo/
下的 zoneinfo 文件的 /etc/localtime
符号链接。如果您选择手动创建链接(例如在 chroot 中,timedatectl
将不起作用),请记住它必须是符号链接,如 localtime(5) § 描述 中指定的那样
# 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) 漂移。
- 网络时间协议守护进程 — 该协议的 参考实现。
- 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(精确时间协议)/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 时间写入硬件时钟。