系统时间

出自 ArchWiki
(重定向自 时区

此文章或章节需要扩充。

原因: 本文主要介绍 systemd timedatectl;首先解释基本命令,如 datehwclock (在 Talk:System time 中讨论)

在操作系统中,时间(时钟)由三个部分决定:时间值、本地时间或 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) § 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 添加一个十六进制值为 1DWORD

您可以从以管理员身份运行的命令提示符执行此操作

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 符号链接。如果您选择手动创建链接(例如在 timedatectl 无法工作的 chroot 期间),请记住它必须是符号链接,如 localtime(5) § DESCRIPTION 中指定的那样

# ln -sf /usr/share/zoneinfo/Zone/SubZone /etc/localtime
提示: 也可以使用 tzselect 交互式地选择时区。

有关详细信息,请参阅 timedatectl(1)localtime(5)

基于地理位置设置

注意: 一些桌面环境支持自动时区选择(例如,请参阅 GNOME#日期和时间)。

要根据 IP 地址位置自动设置时区,可以使用地理位置 API 来检索时区,例如 curl https://ipapi.co/timezone,并将输出传递给 timedatectl set-timezone 以进行自动设置。下面列出了一些提供免费或部分免费服务的地理 IP API

每次 NetworkManager 连接到网络时更新时区

此文章或章节是与 NetworkManager#自动设置时区 合并的候选对象。

注释: (在 Talk:System time 中讨论)

创建一个 NetworkManager 调度器脚本 并使其成为可执行文件

/etc/NetworkManager/dispatcher.d/09-timezone
#!/bin/sh
case "$2" in
    up)
        timedatectl set-timezone "$(curl --fail https://ipapi.co/timezone)"
    ;;
esac
提示: 使用 connectivity-change 而不是 up 可以防止在使用 OpenConnect 等客户端连接到 VPN 时更改时区。

或者,工具 tzupdateAUR 根据 IP 地址的地理位置自动设置时区。此 最流行的 IP 地理位置 API 的比较 可能有助于决定在生产中使用哪个 API。

时间偏差

每个时钟都有一个与真实时间不同的值(真实时间的最佳表示是 国际原子时);没有时钟是完美的。基于石英的电子时钟保持的时间不完美,但保持一致的不准确性。这种基本“不准确性”被称为“时间偏差”或“时间漂移”。

当使用 hwclock 设置硬件时钟时,会计算一个新的每日漂移值(以秒为单位)。漂移值是通过使用新设置的值与设置之前的硬件时钟值之间的差异来计算的,同时考虑到先前漂移值的值和上次设置硬件时钟的时间。新的漂移值和设置时钟的时间被写入文件 /etc/adjtime,覆盖先前的值。因此,当运行命令 hwclock --adjust 时,可以调整硬件时钟以补偿漂移;这也发生在关机时,但仅当启用了 hwclock 守护程序时才会发生,因此对于使用 systemd 的 Arch 系统,这种情况不会发生。

注意: 如果在上次设置后不到 24 小时再次设置 hwclock,则不会重新计算漂移,因为 hwclock 认为经过的时间段太短,无法准确计算漂移。

如果硬件时钟以较大的增量持续丢失或获得时间,则可能是记录了无效的漂移(但仅在 hwclock 守护程序正在运行时适用)。如果您错误地设置了硬件时钟时间,或者您的时间标准未与 Windows 或 macOS 安装同步,则可能会发生这种情况。可以通过首先删除文件 /etc/adjtime,然后设置正确的硬件时钟和系统时钟时间来删除漂移值。然后,您应该检查您的时间标准是否正确。

注意: 如果您希望即使在使用 systemd 时也使用存储在 /etc/adjtime 中的漂移值(例如,您不能或不想使用 NTP),则必须定期调用 hwclock --adjust,或许可以创建一个 cron 作业。

软件时钟非常准确,但像大多数时钟一样,它也不是完全准确的,也会漂移。虽然很少见,但如果内核跳过中断,系统时钟也会失去准确性。有一些工具可以提高软件时钟的准确性

时间同步

网络时间协议 (NTP) 是一种用于在分组交换、可变延迟数据网络上同步计算机系统时钟的协议。

网络时间协议 (NTP)

为了获得 RFC 定义的正确 NTP 支持,客户端必须能够合并来自多个服务器的时间,补偿延迟,并跟踪系统(软件)时钟上的漂移。以下是 Arch Linux 可用的 NTP 实现

  • Chrony — 一个客户端和服务器,对漫游友好,专为非始终在线的系统而设计。在大多数情况下,收敛速度比 ntpd 更快、更接近参考。还可以跟踪硬件时钟 (RTC) 漂移。
https://chrony-project.org/ || chrony
https://www.ntp.org/ || ntp
  • NTPsec — NTPd 的一个分支,专注于安全性。工作方式类似,只是抛弃了很多旧代码。
https://ntpsec.org/ || ntpsecAUR

简单网络时间协议 (SNTP)

任何功能少于合适的 NTP 节点的都被认为是简单网络时间协议 (SNTP)。基本的 SNTP 客户端可能只是从单个服务器获取时间并立即设置,而无需跟踪长期漂移。SNTP 提供较低的精度,但占用较少的资源。对于桌面用户和嵌入式工作负载,精度通常足够好,但对于 NTP 服务器来说是不可接受的。以下实现了 SNTP

  • ConnMan — 一个具有 SNTP 支持的轻量级网络管理器。
https://01.org/connman (waybackmachine) || connman
  • ntpclient — 一个简单的命令行 SNTP 客户端。
http://doolittle.icarus.com/ntpclient/ || ntpclientAUR
  • OpenNTPD — OpenBSD 项目的一部分,实现了 SNTP 客户端和服务器。没有闰秒支持。
https://www.openntpd.org/ || openntpd
  • sntp — NTPd 附带的 SNTP 客户端。它取代了 ntpdate,建议在非服务器环境中使用。
https://www.ntp.org/ || ntp
  • systemd-timesyncd — 一个简单的 SNTP 守护程序,仅实现客户端,仅专注于从一个远程服务器查询时间。对于大多数安装来说,它应该绰绰有余。
https://systemd.io/ || systemd

每用户/会话或临时设置

对于某些用例,可能需要在不更改全局系统值的情况下更改时间设置。例如,在开发期间测试依赖于时间的应用程序,或者在从另一个区域远程登录到服务器时调整系统时区。

要使应用程序“看到”与系统日期/时间不同的日期/时间,您可以使用 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 相比,该接口更准确。

chronyntpd 都可以使用虚拟 PTP 设备来同步客户机和主机之间的时间,方法是将该设备配置为好像它是真正的 PTP 参考时钟。

故障排除

时钟显示的值既不是 UTC 也不是本地时间

这可能是由多种原因引起的。例如,如果您的硬件时钟在本地时间上运行,但 timedatectl 设置为假定它在 UTC 中,那么您的时区相对于 UTC 的偏移量实际上会被应用两次,从而导致本地时间和 UTC 的值都错误。

要强制您的时钟恢复到正确的时间,并将正确的 UTC 写入您的硬件时钟,请按照以下步骤操作

  • 设置 ntpd(无需将其作为服务启用)。
  • 正确设置您的时区
  • 运行 ntpd -qg 以手动将您的时钟与网络同步,忽略本地 UTC 和网络 UTC 之间的较大偏差。
  • 运行 hwclock --systohc 以将当前的软件 UTC 时间写入硬件时钟。

参见