Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

Proposal: Support HTTP Date Format

118 views
Skip to first unread message

Yordis Prieto

unread,
Nov 21, 2024, 7:18:50 PM11/21/24
to elixir-lang-core
I came across a PR that required parsing https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date, so the person reached out for a third-party library.

I wonder if Elixir should handle parsing HTTP Date or allow the construction of a Date using the day name (Mon, Tue ...), month name (Jan, Feb), and other formatting from HTTP Date.

Christopher Keele

unread,
Nov 22, 2024, 4:15:43 PM11/22/24
to elixir-lang-core
I believe such an Elixir-friendly tool would be useful, but does not belong in the Elixir language itself.

In the spirit of a slim but extensible core, functionality and especially structs in Elixir stdlib tend to be limited to:

- Things useful to any domain, that can only be realized optimally in the language itself
- Things required by the language tooling itself

For example, you see general things like Range parsing/structs in stdlib because their membership tests work with guards and the in operator, so the language itself has to be able to operate on them. And you see things like the URI parsing and semantic Version structs in the stdlib because they are required for mix to be able to fetch libraries and resolve version constraints.

If Elixir needed to deal with this date format to work, or if they were more general-purpose, there'd be a stronger case for inclusion. As it, it probably belongs in one of the general-purpose HTTP handling libraries as a dependency.

On the other hand, you can always go pouring through the erlang stdlib's much more kitchen-sinky set of tools for these sorts of things to see if functions that accomplish what you want are already available to you from erlang itself, without extra dependencies. For example, I knew that erlang comes with a pretty robust http server/client implementation. I remembered that it has a module called :httpc, so I found the docs for the application that contains it, :inets. I noticed an :http_util module in there, and it seems to have the functionality you want. For Elixir compatibility, you just need to translate between erlang and Elixir, something like:

defmodule HTTPDate do
def now(calendar \\ Calendar.ISO) do
calendar |> DateTime.utc_now() |> from_date_time()
end

def from_date_time(date_time = %DateTime{}) when date_time.utc_offset == 0 do
{
{date_time.year, date_time.month, date_time.day},
{date_time.hour, date_time.minute, date_time.second}
}
|> :httpd_util.rfc1123_date()
end

def from_date_time(other), do: raise("expected a DateTime in UTC (GMT), got: #{inspect(other)}")

def to_date_time(string, calendar \\ Calendar.ISO) do
with {{year, month, day}, {hour, minute, second}} <- :httpd_util.convert_request_date(string),
{:ok, date} <- Date.new(year, month, day, calendar),
{:ok, time} <- Time.new(hour, minute, second, {0, 0}, calendar) do
DateTime.new(date, time, "Etc/UTC")
else
# Normalize :httpd_util.convert_request_date errors
:bad_date -> {:error, :invalid_date}
# Date/Time/DateTime.new errors
{:error, reason} -> {:error, reason}
end
end
end

Wojtek Mach

unread,
Nov 22, 2024, 4:38:41 PM11/22/24
to elixir-l...@googlegroups.com
httpd_util.rfc1123_date/1 encodes a date, I believe this topic is mostly about decoding.

As an http client author I’m +1 for this because it occasionally comes up in the type of work I end up doing.

That being said, I think it’d be more productive to have an actual proposal, what would be the function name, args, and returns values and consideration for how it fits within the standard library.

As an aside, my recommendation would be to instead of bringing in a dependency, copy-pasting this from Plug https://github.com/elixir-plug/plug/blob/v1.16.1/lib/plug/conn/cookies.ex#L99:L139. This, though, might be the primary reason _not_ to add this, it’s easy to copy-paste a rock solid implementation from an authoritative source in Plug.

--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/elixir-lang-core/c17fcb61-9517-4fef-9f88-8290d36b3799n%40googlegroups.com.

Wojtek Mach

unread,
Nov 22, 2024, 4:40:45 PM11/22/24
to elixir-l...@googlegroups.com
Oops, the Plug link I sent is obviously about encoding to that format not decoding from it. It’s late here, sorry about that.

Yordis Prieto

unread,
Nov 22, 2024, 5:30:44 PM11/22/24
to elixir-lang-core
Wojtek and I have the same situation and experience. I created the issue after reviewing https://github.com/elixir-tesla/tesla/pull/639#discussion_r1853107509 and realized that we don't have an established package for this. It sounds like httpd_util is the perfect place for this.
Personally, I would love some alignment more than anything. An organization like Plug, Phoenix, or anyone dealing with HTTP would own a tiny package just for this. I will copy and paste the code for now, but we could share more between Reg, Tesla, Plug ... all these HTTP-related things since the HTTP spec is one.

In terms of specs, it is similar to httpd_util.rfc1123_date; I need clarification on the proposal's format. Do you have a good example I could follow? Otherwise, I will trying to find a reference to lean on

Christopher Keele

unread,
Nov 22, 2024, 5:49:32 PM11/22/24
to elixir-l...@googlegroups.com
> httpd_util.rfc1123_date/1 encodes a date, I believe this topic is mostly about decoding.

:httpd_util.convert_request_date/1 does decoding, FWIW—see the HTTPDate.to_date_time/2 function in the example above. Plug's implementation also seems good and Elixir-ish.

> As an http client author I’m +1 for this because it occasionally comes up in the type of work I end up doing.
>
> That being said, I think it’d be more productive to have an actual proposal, what would be the function name, args, and returns values and consideration for how it fits within the standard library.

It might make sense for the HTTP client authors to band together and make a community-blessed http utils lib to start sharing logic between them and arriving on some commonly-useful signatures, which could be introduced as a Plug dependency as well and later progressed to a stdlib inclusion proposal.

I still feel like including it in stdlib, especially with some satisfactory-ish erlang tools already available that could take PRs, strays from the spirit of a slim but extensible core. But I'm happy to be proven wrong by an HTTP utils library that becomes so capable as to feel essential! See: development of the :json module.


You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/FYVQP44UPoI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/elixir-lang-core/F604B657-E980-45F1-9297-DF409E4E3BAC%40wojtekmach.pl.

José Valim

unread,
Nov 22, 2024, 5:56:49 PM11/22/24
to elixir-l...@googlegroups.com
The other thing about decoding is that, depending on the needs, you can be more or less precise. For example, for an HTTP server, I wouldn't mind parsing the actual weekday.

<<_::3-binary, ", ", day::2-binary, " ", month::3-binary, " ", year::4-binary, " ",
  hour::2-binary, ":", minute::2-binary, ":", second::2-binary, " GMT">> = "Sat, 17 Apr 2010 14:00:00 GMT"
 
month =
  case month do
    "Jan" -> 1
    "Feb" -> 2
    "Mar" -> 3
    "Apr" -> 4
    "May" -> 5
    "Jun" -> 6
    "Jul" -> 7
    "Aug" -> 8
    "Sep" -> 9
    "Oct" -> 10
    "Nov" -> 11
    "Dec" -> 12
  end

DateTime.new!(
  Date.new!(String.to_integer(year), month, String.to_integer(day)),
  Time.new!(String.to_integer(hour), String.to_integer(minute), String.to_integer(second))
)

In such cases the implementation is straightforward. The bulk of it is the mapping of month names to a number. It is so straight-forward I don't think it justifies an addition to core.

I would actually be more interested in adding a function that makes it easier to compute elapsed time from now. The implementation in the linked pull request is not efficient because it is building a current datetime (from Unix seconds) only to subtract from it, which converts it back to Unix seconds. We could skip the whole conversion but it is not your fault you don't, we don't have a decent API for that.



Wojtek Mach

unread,
Nov 22, 2024, 6:04:59 PM11/22/24
to elixir-l...@googlegroups.com
FWIW here’s Req implementation for http date encoding/decoding: https://github.com/wojtekmach/req/blob/5bfbccc698f7639b890d8829cefb5a12903eece0/lib/req/utils.ex#L251:L325. I’m sure decoding can be significantly improved but I’d expect it to be reasonably fast already.

Personally I would not create a package for <100 LOC that can be easily copy pasted around but that’s just me. For this reason while I wouldn’t mind having it in core it’s fine it isn’t. (I’d guess for better or worse, mostly worse lol, it is second most commonly used format, after iso8601, which obviously _is_ in core.)

Regarding a format for proposals I don’t believe there’s one. What I like to do, with varying success, is to send a good old usage examples like:

    iex> Foo.bar()
    :baz

I think that goes a long way.

Do you argue for adding it to Calendar or NaiveDateTime, or DateTime. Should it be called parse_http_date or parse_rfc1123 or something else? Why this and not that? Should we encode as well? If you want to add something I think the onus is on you to try answering those questions.

Yordis Prieto

unread,
Nov 22, 2024, 6:26:44 PM11/22/24
to elixir-lang-core
I am with Wojtek on this one

Yordis Prieto

unread,
Dec 6, 2024, 11:59:40 AM12/6/24
to elixir-lang-core
Hey folks, any resolution you would like to see here? Not sure what I should do next.

José Valim

unread,
Dec 6, 2024, 12:39:47 PM12/6/24
to elixir-l...@googlegroups.com
I am not convinced it belongs in core yet, sorry. But we share excellent and small implementations here you could incorporate when necessary.


Reply all
Reply to author
Forward
0 new messages