GNU Date and several versions of RFC 3339 dates
RFC 3339 is an RFC standard for time strings, which is to say how to represent a timestamp in textual form. In its RFC form, it says that it is for 'timestamps for Internet protocol events', but it's been adopted as a time format well beyond that; for example, some Prometheus tools use it. In its pure RFC 3339 form, it has two advantages: it doesn't use time zone names of any sort, and a RFC 3339 time is written all in one string without any spaces.
A true RFC 3339 version of some local time has two normal representations, a version expressed in UTC ('Zulu') time, with a time zone offset of 00:00, and a version expressed in local time:
These both represent the same time, that of the Unix timestamp 1609466834.
Writing out times in RFC 3339 format is a little bit annoying; either you need a time in UTC or you need to remember your local timezone offset (as of the relevant time, no less). To help make up for this, GNU Date has an option where it can produce RFC 3339 dates. Except that it doesn't:
; date --rfc-3339=seconds 2020-12-31 21:07:14-05:00
RFC 3339 is almost unambiguous here; a date-time is expressed as a date and a time with a 'T' between them (a lower case 't' is also accepted). Unfortunately it provides a little escape hatch that GNU Date has taken advantage of:
NOTE: ISO 8601 defines date and time separated by "T".
Applications using this syntax may choose, for the sake of readability, to specify a full-date and full-time separated by (say) a space character.
This escape hatch is not present in RFC 3339's ABNF grammar or in
its examples, but the regrettable presence of this little paragraph
technically lets GNU Date off the hook. However, other programs
that deal with RFC 3339 dates are not so forgiving and do not follow
this 'may', instead requiring that RFC 3339 dates be given to them
with the T. One large case of these is any program parsing time
strings with Go's
time package, where the RFC 3339
format specifically requires the 'T'.
GNU Date can produce real RFC 3339 time strings with the
--iso-8601=seconds' option (and its manual notes that the only
difference between its ISO 8601 format and its RFC 3339 format is that
'T'). However, it has another peculiarity, although this one is RFC
; date --iso-8601=seconds --utc 2021-01-01T02:07:14+00:00
GNU Date writes out the +00:00 of UTC instead of shortening it to 'Z'. There may be some programs that specifically require RFC 3339 times in UTC with the Z; if there are, they won't accept GNU Date's output here. Opinions may be divided on whether 'Z' or '+00:00' is better; I tend to come down on the size of 'Z'.
Other versions of
date, such as the ones in FreeBSD and OpenBSD, don't have any specific output
option for RFC 3339 dates. Since the standard formatting of
strftime() has no option
for a '[+-]hh:mm' version of the time zone offset (only a version
without the separator, as '
%z'), you cannot use them to produce
RFC 3339 dates in local time. Instead you must remember to always
date -u' and fake the time zone (here I use Z):
$ date -u '+%Y-%m-%dT%H:%M:%SZ' 2021-01-01T02:07:14Z
If you have to do this more than once in a blue moon on a FreeBSD or OpenBSD machine, you're clearly going to either be writing a cover script or perhaps a program (so that you can parse a range of time strings as the source time). Or you could install GNU Date, but then you have to deal with the irritation of its version of RFC 3339.