GNU Date and several versions of RFC 3339 dates

December 31, 2020

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:

2020-12-31T21:07:14-05:00
2021-01-01T02:07:14Z

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 ABNF legal:

; 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 use '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.


Comments on this page:

Honestly, this is where I'd do:

   datestr=$(date --rfc-3339=seconds | tr ' ' T);

since I'd find that easier to read later than the full format string.

By cks at 2021-01-11 00:48:20:

Unfortunately, versions of date where you might need to use the format string also don't support '--rfc-3339' or any equivalent of it. Only GNU Date currently supports that and it also supports '--iso8601=seconds' so you can skip all of the complexity and directly get what you want.

Written on 31 December 2020.
« Some ways to do a Prometheus query as of a given time
An interesting and puzzling bit of Linux server utilization »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Thu Dec 31 21:41:57 2020
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.