How do I generate RFC 7231 HTTP date headers in EK9?
← Date, Time, and Duration · Ref: Q551
EK9 DateTime writes and reads the RFC 7231 HTTP-date used by Date, Last-Modified, Expires and If-Modified-Since headers.
WRITING: rfc7231()
httpDate <- DateTime().now().rfc7231()
Produces the IMF-fixdate form, e.g. 'Sat, 15 Jun 2024 14:30:00 GMT'. rfc7231() always writes the SAME INSTANT in GMT (UTC), whatever the DateTime's own zone or offset - there is no need to convert first:
2024-12-25T00:00:00-05:00.rfc7231() 'Wed, 25 Dec 2024 05:00:00 GMT'
Day and month names are always English, whatever the machine's locale, the day always has two digits, and the zone is always the literal GMT - exactly what RFC 7231 section 7.1.1.1 requires. A year that cannot be written in four digits gives an unset String.
READING: fromRfc7231()
since <- DateTime().fromRfc7231(header)
Accepts all three forms an HTTP recipient must accept, and gives the instant in UTC:
IMF-fixdate 'Sun, 06 Nov 1994 08:49:37 GMT' RFC 850 'Sunday, 06-Nov-94 08:49:37 GMT' (two-digit year: more than 50 years ahead means the previous century) ANSI asctime 'Sun Nov 6 08:49:37 1994'
Anything else - an offset instead of GMT, a one-digit day in IMF-fixdate, a day name that does not match the date, lower-case names, ISO 8601 text - gives an unset DateTime, so test it with ? or a guard. For ISO 8601 text use DateTime(text).
WHERE HTTP DATES ARE USED
Date: response header (when the response was generated) Last-Modified: when the resource was last changed Expires: when the cached response becomes stale If-Modified-Since: conditional request header Retry-After: when to retry a failed request
An HTTP service does not have to write Last-Modified itself: an HTTPResponse that returns lastModified() gets the header, and If-Modified-Since is honoured with a 304, by the runtime.
See Q537 for general date formatting. See Q549 for locale formatting. See Q541 for timezone handling.
Example
defines module qa.rfc7231.http.headers defines program Rfc7231HttpHeadersDemo() stdout <- Stdout() // Date header for the current moment - rfc7231() converts to UTC itself now <- DateTime().now() httpDate <- now.rfc7231() stdout.println(`Date header set: ${httpDate?}`) event <- 2024-06-15T14:30:00Z stdout.println(`Last-Modified: ${event.rfc7231()}`) // Any offset or zone: the same instant is written in GMT nyEvent <- 2024-12-25T00:00:00-05:00 stdout.println(`NY event: ${nyEvent.rfc7231()}`) // Reading a header: all three HTTP-date forms give the same UTC instant for header in ["Sun, 06 Nov 1994 08:49:37 GMT", "Sunday, 06-Nov-94 08:49:37 GMT", "Sun Nov 6 08:49:37 1994"] since <- DateTime().fromRfc7231(header) stdout.println(`Parsed: ${since}`) // Not an HTTP-date: unset notHttp <- DateTime().fromRfc7231("2024-06-15T14:30:00Z") stdout.println(`ISO text is an HTTP-date: ${notHttp?}`) // Is the resource newer than the client's copy? clientCopy <- DateTime().fromRfc7231("Sat, 15 Jun 2024 14:30:00 GMT") stdout.println(`Modified since: ${event > clientCopy}`)
Common mistakes
E50060 — DateTime has no toRFC1123() method. EK9 uses rfc7231() which produces the HTTP-standard date format. See ek9 -h E50060 for details.
Incorrect:
httpDate <- now.toRFC1123()
Correct:
httpDate <- now.rfc7231()
Other ways to ask this
- How do I format dates for HTTP headers?
- How does rfc7231() work in EK9?
- How do I create an HTTP Date header value?
Coming from another language?
Java: DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now(ZoneOffset.UTC)) - which writes a one-digit day and needs the UTC conversion done for it. Python: email.utils.formatdate(usegmt=True) / email.utils.parsedate_to_datetime. JavaScript: new Date().toUTCString() / Date.parse. Go: t.UTC().Format(http.TimeFormat) / http.ParseTime. EK9: dateTime.rfc7231() converts to UTC itself; DateTime().fromRfc7231(text) parses all three HTTP-date forms.
Keywords: fromRfc7231, withSameInstant, utc, header, duration, migrate, api, web, http, if-modified-since, format, parse, date, expires, gmt, timezone, rfc7231, last-modified, time