系统时间

来自 Arch Linux 中文维基

系统中通常存在两种时间:软件时间和硬件时间。一个操作系统通过如下内容确定时间:时间数值、时间标准、时区和夏令时调节(中国已经废止)。本文分别介绍各个部分的定义及如何设置他们.

大部分操作系统的时间管理包括如下方面:

  • 启动时根据硬件时钟设置系统时间
  • 运行时通过时间同步联网校正时间
  • 关机时根据系统时间设置硬件时间

时间标准[编辑 | 编辑源代码]

时间表示有两个标准:localtimeUTC(Coordinated Universal Time) 。localtime 标准则依赖于当前时区。UTC 是与时区无关的全球时间标准。尽管概念上有差别,UTC 和 GMT (格林威治时间) 是一样的。

硬件所使用的时间标准由操作系统设定,Windows 默认使用 localtime,Mac OS 默认使用 UTC ,而其他的 UNIX-like 操作系统各不相同。使用 Linux 时,最好将硬件时钟设置为 UTC 标准,并在所有操作系统中使用。这样 Linux 系统就可以自动调整夏令时设置,而如果使用 localtime 标准那么系统时间不会根据夏令时自动调整。

硬件时钟[编辑 | 编辑源代码]

硬件时钟(即实时时钟 RTC 或 CMOS 时钟)仅能保存:年、月、日、时、分、秒这些时间数值,无法保存时间标准(UTC 或 localtime)。在2016年及以后的 UEFI 固件上支持保存是否使用夏令时。

读取硬件时钟[编辑 | 编辑源代码]

# hwclock --show

将系统时钟设置到硬件时钟[编辑 | 编辑源代码]

下面命令将系统时钟设置到硬件时钟,同时会创建或更新 /etc/adjtime。详情请参考 hwclock(8) § The Adjtime File 和后面的 #Time skew 段落。

# hwclock --systohc

系统时钟[编辑 | 编辑源代码]

系统时钟(即软件时间) 与硬件时间分别维护,保存了:时间、时区和夏令时设置。Linux 内核保存为自 UTC 时间 1970 年1月1日经过的秒数。初始系统时钟是从硬件时间计算得来,计算时会考虑/etc/adjtime的设置。系统启动之后,系统时钟与硬件时钟独立运行,Linux 通过时钟中断计数维护系统时钟。

读取系统时间[编辑 | 编辑源代码]

下面命令可以获得系统时间(同时以 localtime 和 UTC 显示:

$ timedatectl

设置系统时钟[编辑 | 编辑源代码]

设置系统时间的本地时间:

# timedatectl set-time "yyyy-MM-dd hh:mm:ss"

例如:

# timedatectl set-time "1999-01-08 11:45:14"

设置时间为1999年,1月8日,11时45分14秒。

多系统[编辑 | 编辑源代码]

如果机器上安装多个操作系统,硬件使用的是本地时间,可能多个操作系统都进行 夏令时调整,导致时间错乱。计算机在多个时区切换时,也会出现问题。 所以建议多系统用户统一使用 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 驱动时可能会根据系统时钟设置硬件时钟。是否设置依赖于平台、内核版本和内核编译选项。如果进行了设置,此时会假定硬件时钟为 UTC 标准,/sys/class/rtc/rtcN/hctosys(N=0,1,2,..) 会设置成 1。

后面 systemd 会根据/etc/adjtime重新设置。所以使用当地时间作为硬件时钟时,可能在启动过程中导致系统时间反复变更(there is a lot more to it). 所以从 systemd 216 版本开始,如果 RTC 设置为本地时间, systemd 不会将时间回写,因为这样会引起 windows 时间错乱。systemd 也不会将当前时区信息传递给内核,这意味着 FAT 时间戳将统一按 UTC 时间处理u[1].

注意:
  • 使用 timedatectl 需要一个启动的 D-Bus.因此,在chroot (比如在安装系统时) 下可能无法运行 timedatectl.在这些情况下, 你可以回去使用hwclock命令或使用 systemd-nspawn 代替chroot.
  • If /etc/adjtime不存在, systemd 会假设硬件时钟设为 UTC.

Windows 系统使用 UTC[编辑 | 编辑源代码]

如果要支持Windows 双系统启动默认使用地方时),那么一般 RTC 会被设置为地方时。Windows 其实也能处理 UTC,需要修改注册表。建议让 Windows 使用 UTC,而非让 Linux 使用地方时。Windows 使用 UTC 后,请记得禁用 Windows 的时间同步功能,以防 Windows 错误设置硬件时间。如上文所说,Linux 可以使用NTP服务来在线同步硬件时钟。

使用 regedit,新建如下 DWORD 值,并将其值设为十六进制的 1

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

如果 Windows 要求根据夏令时更新时钟,可以允许。时钟仍然是 UTC,仅是显示时间会改变。

设置时间标准后需要重新设置硬件时间和系统时间。 updated

如果你有时间偏移问题,重新安装 tzdata 并再次设置你的时区:

# timedatectl set-timezone America/Los_Angeles

Historical notes[编辑 | 编辑源代码]

For really old Windows, the above method fails, due to Windows bugs. More precisely,

  • For 64-bit versions of Windows 7 and older builds of Windows 10, there was a bug that made it necessary to have a QWORD value with hexadecimal value of 1 instead of a DWORD value. This bug has been fixed in newer builds and now only DWORD works.
  • Before Vista SP2, there is a bug that resets the clock to localtime after resuming from the suspend/hibernation state.
  • For XP and older, there is a bug related to the daylight saving time. See [2] for details.
  • For even older versions of Windows, you might want to read https://www.cl.cam.ac.uk/~mgk25/mswish/ut-rtc.html - the functionality was not even documented nor officially supported then.

For these operating systems, it is recommended to use localtime.

UTC 在Ubuntu的设置[编辑 | 编辑源代码]

Ubuntu及其衍生发行版会在安装时检测计算机上是否存在Windows,若存在则会默认使用localtime。这是为了让Windows用户能够在不修改注册表的情况下,在Ubuntu内看到正确的时间。可以通过上面的方法统一设置为 UTC 时间。

时区[编辑 | 编辑源代码]

检查当前时区:

$ 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

详情请参考 timedatectl(1), localtime(5)

Setting based on geolocation[编辑 | 编辑源代码]

注意: Some desktop environments have support for automatic time zone selection (e.g. see GNOME#Date & time).

To set the timezone automatically based on the IP address location, one can use a geolocation API to retrieve the timezone, for example curl https://ipapi.co/timezone, and pass the output to timedatectl set-timezone for automatic setting. Some geo-IP APIs that provide free or partly free services are listed below:

Update timezone every time NetworkManager connects to a network[编辑 | 编辑源代码]

Create a NetworkManager dispatcher script:

/etc/NetworkManager/dispatcher.d/09-timezone
#!/bin/sh
case "$2" in
    up)
        timedatectl set-timezone "$(curl --fail https://ipapi.co/timezone)"
    ;;
esac
提示:Using connectivity-change instead of up can prevent timezone changes when connecting to VPNs with clients such as OpenConnect.

Alternatively, the tool tzupdateAUR automatically sets the timezone based on the geolocation of the IP address. This comparison of the most popular IP geolocation apis may be helpful in deciding which API to use in production.

时钟偏移[编辑 | 编辑源代码]

最能代表“真实时间”的是国际原子时钟),所有的时钟都是有误差的。电子时钟的时间是不准的,但是一般有固定的偏移。这种于基值的差称为“time skew”或“时间偏移”。用 hwclock 设置硬件时间时,会计算每天偏移的秒数。偏移值是原硬件时间与新设置硬件时间的差,并且考虑上次硬件时间设置时的偏移。新的偏移值会在设置时钟时写到文件 /etc/adjtime

注意: 如果硬件时间值与原值的差小于 24 小时,偏移量不会重新计算,因为时间过短,无法精确设置偏移。

如果硬件时钟总是过快或过慢,可能是计算了错误的偏移值。硬件时钟设置错误或者时间标准与其他操作系统不一致导致。删除文件 /etc/adjtime 可以删除偏移值,然后设置正确的硬件时钟和系统时钟,并检查时间标准是不是设置正确。

注意: 使用 Systemd 时,要使用 /etc/adjtime中的 drift 值(即无法或不想使用 NTP 时); 需要每次调用 hwclock --adjust命令,可以通过 cron 任务实现。

提高系统时间精度的方法有:

  • NTP 可以通过网络时间协议同步 Linux 系统的时间。NTP 也会修正中断频率和每秒滴答数以减少时间偏移。并且每隔 11 分钟同步一次硬件时钟。

时钟同步[编辑 | 编辑源代码]

网络时间协议 (NTP) 是一个通过包交换和可变延迟网络来同步计算机系统时间的协议。下列为这个协议的实现:

  • Chrony — 是一个客户端和服务器,更适合漫游,是为不能始终保持在线的系统而特别设计。
https://chrony.tuxfamily.org/ || chrony
  • ConnMan — A lightweight network manager with NTP support.
https://01.org/connman (waybackmachine) || connman
  • Network Time Protocol daemon — 是这个协议的参考实现,推荐用于时间服务器。它也可以调节中断频率和每秒滴答次数以减少系统时钟误差,使得硬件时钟每隔11秒重新同步一次。
https://www.ntp.org/ || ntp
  • ntpclient — 是简单的命令行 NTP 客户端。
http://doolittle.icarus.com/ntpclient/ || ntpclientAUR
  • NTPsec — A fork of NTPd, focused on security.
https://ntpsec.org/ || ntpsecAUR
  • OpenNTPD — 是 OpenBSD 项目的一部分,同时实现了客户端和服务器。
https://www.openntpd.org/ || openntpd
  • sntpntp 包里附带的一个 SNTP 客户端。它取代了 ntpdate ,并被推荐用于非服务器环境。
https://www.ntp.org/ || ntp
  • systemd-timesyncd — 是一个简单的 SNTP 守护进程。它只实现了客户端,专用于从远程服务器查询时间,更适用于绝大部分安装的情形。
https://www.freedesktop.org/wiki/Software/systemd/ || systemd

Per-user/会话或临时设置[编辑 | 编辑源代码]

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 and enable the service fake-hwclock.service.

故障排除[编辑 | 编辑源代码]

时间显示的既不是UTC也不是本地时间[编辑 | 编辑源代码]

这可能是由多种原因造成的。假如说你的硬件时间使用的是localtime,但timedatectl被设置为使用UTC,结果是你的时区偏移设置被应用了两次,导致系统显示的既不是UTC也不是本地时间。

要强制校准你的时钟,并且让系统写入正确的硬件时间,遵循下面几步:

  • 设置好ntpd(不需要将其作为一个服务来运行);
  • 正确设置你的时区
  • 运行ntpd -qg,此时在系统和网络进行时间校正时,会忽略本地UTC时间与网络UTC时间之间过大的差异;
  • 运行hwclock --systohc,将当前的正确UTC时间写入硬件时间。

外部资源[编辑 | 编辑源代码]