Skip to content

108.1 Maintain system time

Weight: 3

Candidates should be able to properly maintain the system time and synchronize the clock via NTP.

Objectives

  • Set the system date and time.
  • Set the hardware clock to the correct time in UTC.
  • Configure the correct timezone.
  • Basic NTP configuration using ntpd and chrony.
  • Knowledge of using the pool.ntp.org service.
  • Awareness of the ntpq command.

Terms

/usr/share/zoneinfo/, /etc/timezone, /etc/localtime, /etc/ntp.conf, /etc/chrony.conf, date, hwclock, ntpd, ntpdate, chronyc, pool.ntp.org

Two clocks

A Linux computer keeps time with two clocks:

Clock Where Runs
hardware clock (or real-time clock, RTC) a chip on the motherboard with its own battery all the time, even when the computer is off
system (software) clock kept by the kernel in memory only while Linux is running

At boot, the system clock is set from the hardware clock. After that, the two run independently and can slowly drift apart. Programs always use the system clock.

 power off                boot                         running

 hardware clock  ------>  system clock set from it --> both tick separately
 (battery)                                              NTP keeps the system clock right
                                                        hwclock --systohc copies it back

On most modern systems, NTP (the Network Time Protocol) keeps the time correct for you, and the only thing a user sets is the time zone.

UTC and local time

The system clock counts time in UTC (Coordinated Universal Time, the time in Greenwich, UK). Local time is calculated from UTC by adding the offset of your time zone and any daylight saving time. Keeping the clock in UTC avoids a lot of trouble, because time zones and daylight saving rules change, sometimes by political decision.

The hardware clock should also be kept in UTC. It can be set to local time instead, which older operating systems did, but UTC is recommended.

date

date shows the system time as local time. -u shows UTC:

$ date
Sun Nov 17 12:55:06 EST 2019
$ date -u
Sun Nov 17 18:02:51 UTC 2019

Output formats

Option Prints
-I ISO 8601 format. -Idate gives the date only. Also hours, minutes, seconds, ns
-R RFC 5322 format
--rfc-3339=... RFC 3339 format
+FORMAT your own format, built from codes like %Y (year), %m (month), %d (day), %H (hour), %M (minute), %T (time), %s (Unix time)
$ date +%s
1574014515

Unix time (or epoch time) is how most Unix-like systems store time inside: the number of seconds since 1 January 1970, UTC. Stored in 32 bits, it runs out on 19 January 2038, a known problem for old 32-bit systems.

--date (-d) shows a time other than now, for example converting Unix time back into a date. --debug shows step by step how date understood your input, which helps with an application that writes odd dates:

$ date --date='@1564013011'
Wed Jul 24 20:03:31 EDT 2019
$ date --debug --date="Fri, 03 Jan 2020 14:00:17 -0500"
date: parsed day part: Fri (day ordinal=0 number=5)
date: parsed date part: (Y-M-D) 2020-01-03
date: parsed time part: 14:00:17 UTC-05
(...)
date: final: 1578078017.000000000 (epoch-seconds)
date: final: (Y-M-D) 2020-01-03 19:00:17 (UTC)
date: final: (Y-M-D) 2020-01-03 14:00:17 (UTC-05)

Setting the date

--set (or -s) sets the system clock. This needs root:

# date --set="11 Nov 2011 11:11:11"
# date +%Y%m%d -s "20111125"        # date only: the format says how to read the text
# date +%T -s "13:11:00"            # time only

hwclock

hwclock reads and sets the hardware clock. It needs root. It shows the time converted to local time, even when the hardware clock is kept in UTC:

$ sudo hwclock
2019-11-20 11:31:29.217627-05:00

Changing the system time does not touch the hardware clock. Here the system clock is moved to 2022, but hwclock still shows the real time:

$ date
Fri Jun 23 01:47:22 PM +0330 2023
$ sudo date -s "Jan 22 22:22:22 2022"
Sat Jan 22 10:22:22 PM +0330 2022
$ sudo hwclock
2023-06-23 13:47:41.160122+03:30

--verbose shows the details, including the device (/dev/rtc0), whether the clock is kept in UTC, and the drift between the two clocks:

$ sudo hwclock --verbose
hwclock from util-linux 2.34
System Time: 1578079387.976029
Trying to open: /dev/rtc0
Using the rtc interface to the clock.
Assuming hardware clock is kept in UTC time.
(...)
Calculated Hardware Clock drift is 0.000000 seconds
2020-01-03 14:23:07.948436-05:00

Copying between the clocks:

Command Short form Copies
hwclock --systohc hwclock -w system clock to hardware clock
hwclock --hctosys hwclock -s hardware clock to system clock

After you change the system time, run hwclock --systohc so both clocks agree. Or go the other way: set the hardware clock, then copy it into the system clock:

# hwclock --set --date "4/12/2019 11:15:19"
# hwclock
Fri 12 Apr 2019 6:15:19 AM EST -0.562862 seconds
# hwclock --hctosys

hwclock --set expects UTC by default and shows local time, which is why 11:15 became 6:15 EST. To say which one the hardware clock uses, add -u (--utc) or --localtime:

# hwclock --localtime --set --date="01/05/2023 22:04:00"   # keep the hardware clock in local time
# hwclock -u -w                                            # back to UTC, set from the system clock

The choice is remembered in /etc/adjtime. When you say neither, /etc/adjtime decides, and if that file does not exist, UTC is used.

timedatectl

On systemd systems, timedatectl is the preferred tool. It shows local time, UTC, the hardware (RTC) time, the time zone and the NTP status at once:

$ timedatectl
               Local time: Thu 2019-12-05 11:08:05 EST
           Universal time: Thu 2019-12-05 16:08:05 UTC
                 RTC time: Thu 2019-12-05 16:08:05
                Time zone: America/Toronto (EST, -0500)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

RTC in local TZ: no means the hardware clock is in UTC, as it should be.

Command Does
timedatectl set-time '2011-11-25 14:00:00' set date and time (or just HH:MM:SS)
timedatectl list-timezones list the time zone names (long: use grep)
timedatectl set-timezone Africa/Cairo set the time zone
timedatectl set-ntp no turn NTP synchronization off (yes or true turns it on: timedatectl set-ntp true)

When NTP is not available, timedatectl set-time is recommended over date and hwclock. date and hwclock remain important for older systems.

Time zone names must be exact: Africa/Cairo works, cairo or africa/cairo does not.

$ timedatectl set-timezone Africa/Cairo
$ timedatectl
               Local time: Thu 2019-12-05 18:18:10 EET
           Universal time: Thu 2019-12-05 16:18:10 UTC
                 RTC time: Thu 2019-12-05 16:18:10
                Time zone: Africa/Cairo (EET, +0200)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Universal time did not change, only local time did.

Time zones without timedatectl

Time zone rules are stored in /usr/share/zoneinfo/, in subdirectories named after continents: /usr/share/zoneinfo/Europe/Paris, /usr/share/zoneinfo/Asia/Tehran. Each file holds the offset from UTC and the daylight saving rules.

Linux reads the current zone from /etc/localtime. It is a binary file, usually a symbolic link into /usr/share/zoneinfo/, so it stays right when the zone data is updated:

$ ls -l /etc/localtime
lrwxrwxrwx 1 root root 31 Jun 22 11:19 /etc/localtime -> /usr/share/zoneinfo/Asia/Tehran
$ file /usr/share/zoneinfo/Asia/Tehran
/usr/share/zoneinfo/Asia/Tehran: timezone data, version 2, no gmt time flags, no std time flags, no leap seconds, 72 transition times, 8 abbreviation chars

To change the zone by hand, point the link at your zone, then update the hardware clock:

# ln -sf /usr/share/zoneinfo/Canada/Eastern /etc/localtime
# hwclock --systohc

Some distributions also have /etc/timezone, a plain text file with the zone's name, which programs can read to show it:

$ cat /etc/timezone
America/Toronto
File Is Used by
/usr/share/zoneinfo/ all time zone data the system
/etc/localtime the current zone, binary, usually a link the system, to calculate local time
/etc/timezone the current zone's name, text some distributions and programs

NTP

The most accurate time comes from reference clocks, usually atomic clocks. NTP (Network Time Protocol) lets every computer on the internet synchronize to them. NTP is organised in levels called strata:

 reference clocks (atomic clocks)
         |
 stratum 1   servers attached to them        usually not public
         |
 stratum 2   servers synced from stratum 1   public
         |
 stratum 3   ... and so on

In a large network, let a few machines sync with public stratum 2 servers and serve time to all the others, so the public servers carry less load.

Terms you will see in the tools:

Term Means
offset the difference between system time and NTP time. 12:00:02 against 11:59:58 is an offset of 4 seconds
slew an offset under 128 ms is corrected gradually, by speeding up or slowing down the clock
step an offset over 128 ms is corrected in one jump
insane time an offset over 17 minutes: the NTP daemon refuses to change the clock. You must fix it by hand first
drift two clocks that slowly move apart over time
jitter the drift since the last time the clock was checked

NTP works on port 123 (UDP). If synchronization fails, check that this port is open.

pool.ntp.org

pool.ntp.org is a free, volunteer-run pool of public NTP servers. Each time you ask it, you get a different server at random, which spreads the load. Distributions use their own names in the pool, like 0.debian.pool.ntp.org or 0.centos.pool.ntp.org. For work where wrong time could cause real harm, the pool's own advice is to set up a local, reliable time service instead.

systemd-timesyncd

On systemd systems, timedatectl uses the systemd-timesyncd service, which is a simple SNTP client: it keeps this machine on time, but cannot serve time to other machines. It only works while the service runs:

$ systemctl status systemd-timesyncd
● systemd-timesyncd.service - Network Time Synchronization
     Loaded: loaded (/lib/systemd/system/systemd-timesyncd.service; enabled; vendor preset: enabled)
     Active: active (running) since Thu 2020-01-09 21:01:50 EST; 2 weeks 1 days ago
     Status: "Synchronized to time server for the first time 91.189.89.198:123 (ntp.ubuntu.com)."

timedatectl show-timesync --all shows its details. That is enough for a single machine, but to sync many clients in a network, install a full NTP implementation: ntpd or chrony.

ntpd

ntpd is the classic NTP daemon. It runs in the background, compares the clock with NTP servers on a schedule, and can also serve time to other machines. Run only one of systemd-timesyncd, ntpd and chrony at a time. Install the ntp package, then start and enable the service (named ntp on Debian, ntpd on Red Hat systems):

# apt install ntp                               # Debian
# systemctl start ntp
# systemctl enable ntpd && systemctl start ntpd  # Red Hat
$ systemctl status ntpd
● ntpd.service - Network Time Service
   Loaded: loaded (/usr/lib/systemd/system/ntpd.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2019-12-06 03:27:21 EST; 7h ago
 Main PID: 867 (ntpd)

It asks several sources and picks the best ones. If the network is lost, it uses its history to keep adjusting the clock.

/etc/ntp.conf

The servers to use are listed in /etc/ntp.conf:

# Use public servers from the pool.ntp.org project.
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst
Line Means
server 192.168.1.1 always ask this one server (an IP address, or a name if DNS works)
pool 0.debian.pool.ntp.org ask a pool, which hands out a different server each time
iburst send a quick burst of requests at startup, for a faster first sync
driftfile /var/lib/ntp/ntp.drift where ntpd remembers how fast this clock drifts
restrict ... access rules: who may ask this server for time, or change it

On Debian the defaults look like this:

driftfile /var/lib/ntp/ntp.drift

pool 0.debian.pool.ntp.org iburst
pool 1.debian.pool.ntp.org iburst
pool 2.debian.pool.ntp.org iburst
pool 3.debian.pool.ntp.org iburst

# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

Restart the service after changing the file.

ntpdate

ntpdate sets the clock once, right now, from a server:

$ sudo ntpdate pool.ntp.org
23 Jun 14:49:55 ntpdate[160138]: adjust time server 31.214.170.254 offset +0.020196 sec

Its main use: when the clock is more than 17 minutes off (insane time), ntpd will not fix it, so you fix it by hand first:

  1. Stop the daemon: systemctl stop ntpd. While it runs, it holds the NTP port and ntpdate fails:

    # ntpdate pool.ntp.org
    23 Jun 14:49:55 ntpdate[18670]: the NTP socket is in use, exiting
    
  2. Run ntpdate pool.ntp.org, more than once if needed.

  3. Save it to the hardware clock with hwclock -w, and start ntpd again.

ntpq

ntpq queries the NTP daemon to check its status. -p prints the servers (peers) it is using, and -n shows IP addresses instead of names. Without options it starts an interactive mode, where ? lists the commands:

$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+37.44.185.42    91.189.94.4      3 u   86  128  377  126.509  -20.398   6.838
+ntp2.0x00.lv    193.204.114.233  2 u   82  128  377  143.885   -8.105   8.478
*inspektor-vlan1 121.131.112.137  2 u   17  128  377  112.878  -23.619   7.959
 b1-66er.matrix. 18.26.4.105      2 u  484  128   10   34.907   -0.811  16.123
Column Means
remote the NTP server
refid that server's own reference
st its stratum
when seconds since the last query
poll seconds between queries
reach whether the server answered; grows with each success
delay round trip time in ms
offset difference between system time and the server, in ms
jitter the variation since the last query, in ms

The first character of each line is important: * marks the server currently used as the main reference, + a good candidate, and - a server out of range that is ignored.

chrony

chrony is a newer NTP implementation. It handles difficult conditions better than ntpd, like a laptop that is often offline or a busy network, and it is the default on Red Hat 8, SUSE 15 and many other distributions. It has two parts:

  • chronyd, the daemon. Start and enable it first.
  • chronyc, the command line client. It talks to chronyd over TCP or Unix sockets, so it can even monitor a remote chronyd, though remotely it is limited to mostly monitoring commands, for security.

chronyc tracking

$ chronyc tracking
Reference ID    : 3265FB3D (bras-vprn-toroon2638w-lp130-11-50-101-251-61.dsl.)
Stratum         : 3
Ref time (UTC)  : Thu Jan 09 19:18:35 2020
System time     : 0.000134029 seconds fast of NTP time
Last offset     : +0.000166506 seconds
RMS offset      : 0.000470712 seconds
Frequency       : 919.818 ppm slow
Residual freq   : +0.078 ppm
Skew            : 0.555 ppm
Root delay      : 0.006151616 seconds
Root dispersion : 0.010947504 seconds
Update interval : 129.8 seconds
Leap status     : Normal
Line Means
Reference ID the server we are synced to
Stratum the number of hops to a reference clock
System time how far the system clock is from NTP time
Last offset the offset at the last update
RMS offset the long-term average offset
Frequency how wrong the clock would run without correction, in ppm (parts per million)
Leap status Normal, Insert second, Delete second or Not synchronised

chronyc sources and other commands

Command Shows or does
chronyc tracking the sync status, above
chronyc sources the NTP servers in use
chronyc activity how many sources are online and offline
chronyc ntpdata details of the last NTP update
chronyc makestep steps the clock now, in one jump
chronyc alone an interactive chronyc> prompt for the same commands
$ chronyc sources
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^+ brazil.time.system76.com      2   7   377    20    -13ms[  -13ms] +/-  190ms
^+ ohio.time.system76.com        2   7   377    21    +23ms[  +23ms] +/-  171ms
^* paris.time.system76.com       2   7   377    24  -4880us[-1560us] +/-  109ms

As in ntpq, * is the server in use and + a good candidate.

/etc/chrony.conf

chrony's configuration is /etc/chrony.conf (/etc/chrony/chrony.conf on Debian and Ubuntu). The servers use the same syntax as ntpd. In this example from Arch Linux the lines start with !, chrony's comment mark, so no source is active yet:

! server 0.arch.pool.ntp.org iburst
! server 1.arch.pool.ntp.org iburst
! server 2.arch.pool.ntp.org iburst
! pool 3.arch.pool.ntp.org iburst
! makestep 1.0 3

Remove the ! to use them. makestep 1.0 3 lets chrony step the clock when it is off by more than 1 second, during the first 3 updates, which fixes a large error at startup. The same file sets the driftfile and keyfile. After editing, restart chronyd, run chronyc makestep to correct the clock at once, and check the result with chronyc tracking:

# chronyc makestep
200 OK

More examples

The same commands once more, with other names and values, as a quick reference:

$ date
Fri Jun 23 01:47:22 PM +0330 2023
$ date -u                                 # the same moment in UTC
$ date +%s                                # Unix epoch: seconds since 1 Jan 1970
$ sudo date -s "Jan 22 22:22:22 2022"     # move the system clock
Sat Jan 22 10:22:22 PM +0330 2022
$ sudo hwclock                            # hardware clock is unchanged
2023-06-23 13:47:41 +03:30
# hwclock -w         # (--systohc) set the hardware clock FROM the system clock
# hwclock -s         # (--hctosys) set the system clock FROM the hardware clock
# hwclock -u -w      # write it, and record that the hardware clock is in UTC
# hwclock --set --date "2024-01-22 22:22:22"   # set the hardware clock to a given time
$ ls -l /etc/localtime
lrwxrwxrwx 1 root root 31 Jun 22 11:19 /etc/localtime -> /usr/share/zoneinfo/Asia/Tehran
# ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime   # set it by hand
$ sudo ntpdate pool.ntp.org
23 Jun 14:49:55 ntpdate[160138]: adjust time server 31.214.170.254 offset +0.020196 sec
$ sudo hwclock -w
$ ntpq -p
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
+46.209.14.1     192.168.5.2      4 u    7   64    1   58.300  -15.546  14.519
*194.225.150.25  194.190.168.1    2 u    5   64    1   31.478   -3.870  95.635
-ntp.tums.ac.ir  195.161.115.4    4 u    4   64    1   30.636    2.485   4.025
$ chronyc tracking          # how well the clock is synced
Reference ID    : 0FED61D6 (paris.time.system76.com)
Stratum         : 3
System time     : 0.001574947 seconds slow of NTP time
Leap status     : Normal
$ chronyc sources           # the servers in use, with * for the selected one
$ sudo chronyc makestep     # jump the clock to the correct time now

Summary

I keep two clocks straight. Linux has a hardware clock on the motherboard, which runs on battery and is read once at boot to set the system clock in the kernel, and then the two run separately. Both should be in UTC, and local time is UTC plus the time zone offset. date shows local time (-u for UTC, +%s for Unix time, seconds since 1970) and date -s sets the system clock. hwclock reads the hardware clock, --systohc (-w) copies the system time into it, so after setting the time by hand I run hwclock -w, --hctosys does the reverse, and -u or --localtime says how it is kept, remembered in /etc/adjtime. On systemd systems, timedatectl shows everything and sets the time, the time zone and NTP.

Time zone data lives in /usr/share/zoneinfo/, the active zone is /etc/localtime, usually a symbolic link into it, and some systems also keep its plain name in /etc/timezone. timedatectl set-timezone changes it, and by hand I relink /etc/localtime and run hwclock --systohc.

To stay accurate automatically I use NTP, which syncs clocks through a hierarchy of strata down from atomic clocks, on port 123. Small offsets are slewed gradually, larger ones stepped, and an offset over 17 minutes is insane time that the daemon will not touch, so I stop it and run ntpdate pool.ntp.org by hand for a one-shot correction. pool.ntp.org hands out a random volunteer server each time. systemd-timesyncd is a simple SNTP client for one machine, while ntpd is a full service configured in /etc/ntp.conf with server and pool lines, and ntpq -p shows its peers, with * marking the chosen one. chrony is the modern alternative that copes better with laptops and flaky networks: chronyd runs as the daemon, it is configured in /etc/chrony.conf, and chronyc tracking, sources and makestep check and correct the clock.