27 September 2021
You can use the date
utility to print, format and convert a timestamp. Historically the utility was also used to set the system time, though that is nowadays usually done via timedatectl
.
One of the first things you might notice when you run the date
command is that the timestamp is a bit odd. This is the default format:
$ date Thu 23 Sep 17:31:03 BST 2021
The month is shown as an abbreviated name, which presumably is because some countries put the month before the date (January 1st instead of 1st January). I don’t think there is a particular reason why the year appears after the timestamp. Luckily, you can format the timestamps any way you like.
To format a timestamp you specify a format string. The string start with a plus sign followed by one or more control characters (in quotes). For instance, you can print the current time in HH:MM:SS format using the following command:
$ date +'%H:%M:%S' 17:31:23
There are quite a control characters. As always, check the documentation (man 1 date
) if you want a full list. For convenience, here are the most common format strings.
Char | Description | Example |
---|---|---|
%a | Short weekday name | Mon, Tue etc. |
%A | Full weekday name | Monday, Tuesday etc. |
%b | Short month name | Jan, Feb etc. |
%B | Full month name | January, February etc. |
%d | Day or the month | 01 to 31 |
%H | Two digit hour (24 hour clock) | 00 to 23 |
%I | Two digit hour (12 hour clock) | 01 to 12 |
%j | Day of the year | 001 to 365 |
%m | Two digit month | 01 to 12 |
%M | Two digit minutes | 00 to 59 |
%s | Seconds since Unix epoch | 1632734437 |
%u | Day of week (Monday is 1) | 1 to 7 |
%w | Day of week (Sunday is 0 | 0 to 6 |
%W | Week number | 00 to 53 |
%Y | Year | 2021 |
%Z | Short time zone | BST, CET |
%F | Short for %Y-%m-%d | 2021-09-27 |
%T | Short for %H:%M:%S | 10:26:58 |
%R | Short for %H:%M | 10:26 |
So, the default timestamp is formatted as follows:
$ date '+%a %d %b %T %Z %Y' Thu 23 Sep 17:56:56 BST 2021
And if you prefer something more intuitive then you can use a format string like this:
$ date '+%F %T %Z' 2021-09-23 17:57:39 BST
Operating systems and software applications often use a more or less random date from which it can measure time in seconds. Unix systems, including Linux, use the start of 1 January 1970 (UTC) as the epoch.
The Unix time is handy in lots of situations. For instance, when we look into website errors we often use a script that retrieves log entries in the last x minutes. To make that work the script converts timestamps to Unix times, and it then retrieves all entries in the last x seconds. Without converting timestamps to Unix times that isn’t possible.
The %s
control character prints a Unix time. You can add the -d
(--date
) option to print a specific date as the number of seconds since 1 January 1970:
$ date '+%s' --date '2020-01-01 00:00:00 UTC' 1577836800 $ date '+%s' --date '1970-01-01 UTC' 0 $ date '+%s' --date '1900-01-01 UTC' -2208988800
Note that the time in the first command was redundant – if you don’t specify the time the command assumes you want to know the time at the start of the day. Also, dates prior to 1 January 1970 are always negative.
As an aside, the Unix timestamp is stored as a 32-bit integer. That is a problem, as it means we can’t count beyond 19 January 2038 (03:14:07 to be precise). This is a bit like the year 2000 problem. The issue is being addressed, but there might be some smart doorbells that start misbehaving in early 2038.
The --date
option is quite flexible. I typically specify dates in the format YYYY-MM-DD HH:MM:SS TZ, just because that makes sense to me. However, the utility is smart enough to understand and convert lots of different timestamps. For instance, user error logs on Apache servers typically contain timestamps using the format DD-MMM-YYYY HH:MM:SS TZ. Those timestamps can be converted on the fly:
$ date "+%s" --date '01-Jan-2020 00:00:00 UTC' 1577836800 $ date --date @1577836800 +"%F %T %Z" 2020-01-01 00:00:00 GMT $ date "+%s" --date '01-Jan-2020 00:00:00 CET' 1577833200 $ date --date @1577833200 +"%F %T %Z" 2019-12-31 23:00:00 GMT
Similarly, entries in the global Apache error log are formatted as DDD MMM DD HH:MM:SS.MS YYYY. Again, the timestamps can be converted without performing magic:
$ date "+%s" --date 'Wed Jan 01 00:00:00.00000 2020' 1577836800 $ date "+%s" --date 'Wed Jan 01 00:00:00.00000 2020 CET' 1577833200
Other timestamps can’t be easily converted. For instance, the date
utility doesn’t understand Apache access log timestamps:
$ date "+%s" --date '01/Jan/2020:00:00:00' date: invalid date ‘01/Jan/2020:00:00:00’
There are more limitations. If you want to convert all timestamps in a log file then you need to use an alternative tool, such as python or awk.
One thing to always bear in mind is that date
uses the system’s timezone by default. As I write it is British Summer Time:
$ date Fri 24 Sep 20:28:16 BST 2021
You can get more detailed information about the system time using timedatectl
, which is part of systemd:
$ timedatectl Local time: Fri 2021-09-24 20:27:02 BST Universal time: Fri 2021-09-24 19:27:02 UTC RTC time: Fri 2021-09-24 19:27:02 Time zone: Europe/London (BST, +0100) System clock synchronized: yes NTP service: active RTC in local TZ: no
As already shown, you can always specify the time zone. You can even do things like this:
$ TZ='Europe/Amsterdam' date +'%H:%M' 21:27
The command prints the current time in another time zone. You can do this with any of the time zones listed in /usr/share/zoneinfo/.
When you edit a file it is usually a good idea to make a copy of the original file first. For instance, before you change your website’s .htaccess file you can make a copy, so that you can easily undo your changes if needed. Rather than naming the copy something like .htaccess.bak you can append the current date:
$ cp .htaccess .htaccess_$(date "+%Y%m%d%H%M%S") $ ls -1AF .htaccess* .htaccess .htaccess_20210924221735
The $(...)
syntax is called command substitution. In this case I put a date
command inside the brackets, which appends the current timestamp. That way you can quickly see the when the copy was made.