cow_date.erl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. %% Copyright (c) 2013-2023, Loïc Hoguin <essen@ninenines.eu>
  2. %%
  3. %% Permission to use, copy, modify, and/or distribute this software for any
  4. %% purpose with or without fee is hereby granted, provided that the above
  5. %% copyright notice and this permission notice appear in all copies.
  6. %%
  7. %% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. %% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. %% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  10. %% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. %% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  12. %% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  13. %% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  14. -module(cow_date).
  15. -export([parse_date/1]).
  16. -export([rfc1123/1]).
  17. -export([rfc2109/1]).
  18. -export([rfc7231/1]).
  19. -ifdef(TEST).
  20. -include_lib("proper/include/proper.hrl").
  21. -endif.
  22. %% @doc Parse the HTTP date (IMF-fixdate, rfc850, asctime).
  23. -define(DIGITS(A, B), ((A - $0) * 10 + (B - $0))).
  24. -define(DIGITS(A, B, C, D), ((A - $0) * 1000 + (B - $0) * 100 + (C - $0) * 10 + (D - $0))).
  25. -spec parse_date(binary()) -> calendar:datetime().
  26. parse_date(DateBin) ->
  27. Date = {{_, _, D}, {H, M, S}} = http_date(DateBin),
  28. true = D >= 0 andalso D =< 31,
  29. true = H >= 0 andalso H =< 23,
  30. true = M >= 0 andalso M =< 59,
  31. true = S >= 0 andalso S =< 60, %% Leap second.
  32. Date.
  33. http_date(<<"Mon, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  34. http_date(<<"Tue, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  35. http_date(<<"Wed, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  36. http_date(<<"Thu, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  37. http_date(<<"Fri, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  38. http_date(<<"Sat, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  39. http_date(<<"Sun, ", D1, D2, " ", R/bits >>) -> fixdate(R, ?DIGITS(D1, D2));
  40. http_date(<<"Monday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  41. http_date(<<"Tuesday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  42. http_date(<<"Wednesday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  43. http_date(<<"Thursday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  44. http_date(<<"Friday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  45. http_date(<<"Saturday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  46. http_date(<<"Sunday, ", D1, D2, "-", R/bits >>) -> rfc850_date(R, ?DIGITS(D1, D2));
  47. http_date(<<"Mon ", R/bits >>) -> asctime_date(R);
  48. http_date(<<"Tue ", R/bits >>) -> asctime_date(R);
  49. http_date(<<"Wed ", R/bits >>) -> asctime_date(R);
  50. http_date(<<"Thu ", R/bits >>) -> asctime_date(R);
  51. http_date(<<"Fri ", R/bits >>) -> asctime_date(R);
  52. http_date(<<"Sat ", R/bits >>) -> asctime_date(R);
  53. http_date(<<"Sun ", R/bits >>) -> asctime_date(R).
  54. fixdate(<<"Jan ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  55. {{?DIGITS(Y1, Y2, Y3, Y4), 1, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  56. fixdate(<<"Feb ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  57. {{?DIGITS(Y1, Y2, Y3, Y4), 2, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  58. fixdate(<<"Mar ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  59. {{?DIGITS(Y1, Y2, Y3, Y4), 3, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  60. fixdate(<<"Apr ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  61. {{?DIGITS(Y1, Y2, Y3, Y4), 4, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  62. fixdate(<<"May ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  63. {{?DIGITS(Y1, Y2, Y3, Y4), 5, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  64. fixdate(<<"Jun ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  65. {{?DIGITS(Y1, Y2, Y3, Y4), 6, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  66. fixdate(<<"Jul ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  67. {{?DIGITS(Y1, Y2, Y3, Y4), 7, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  68. fixdate(<<"Aug ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  69. {{?DIGITS(Y1, Y2, Y3, Y4), 8, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  70. fixdate(<<"Sep ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  71. {{?DIGITS(Y1, Y2, Y3, Y4), 9, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  72. fixdate(<<"Oct ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  73. {{?DIGITS(Y1, Y2, Y3, Y4), 10, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  74. fixdate(<<"Nov ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  75. {{?DIGITS(Y1, Y2, Y3, Y4), 11, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  76. fixdate(<<"Dec ", Y1, Y2, Y3, Y4, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  77. {{?DIGITS(Y1, Y2, Y3, Y4), 12, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}}.
  78. rfc850_date(<<"Jan-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  79. {{rfc850_year(?DIGITS(Y1, Y2)), 1, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  80. rfc850_date(<<"Feb-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  81. {{rfc850_year(?DIGITS(Y1, Y2)), 2, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  82. rfc850_date(<<"Mar-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  83. {{rfc850_year(?DIGITS(Y1, Y2)), 3, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  84. rfc850_date(<<"Apr-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  85. {{rfc850_year(?DIGITS(Y1, Y2)), 4, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  86. rfc850_date(<<"May-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  87. {{rfc850_year(?DIGITS(Y1, Y2)), 5, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  88. rfc850_date(<<"Jun-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  89. {{rfc850_year(?DIGITS(Y1, Y2)), 6, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  90. rfc850_date(<<"Jul-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  91. {{rfc850_year(?DIGITS(Y1, Y2)), 7, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  92. rfc850_date(<<"Aug-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  93. {{rfc850_year(?DIGITS(Y1, Y2)), 8, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  94. rfc850_date(<<"Sep-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  95. {{rfc850_year(?DIGITS(Y1, Y2)), 9, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  96. rfc850_date(<<"Oct-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  97. {{rfc850_year(?DIGITS(Y1, Y2)), 10, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  98. rfc850_date(<<"Nov-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  99. {{rfc850_year(?DIGITS(Y1, Y2)), 11, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  100. rfc850_date(<<"Dec-", Y1, Y2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " GMT">>, Day) ->
  101. {{rfc850_year(?DIGITS(Y1, Y2)), 12, Day}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}}.
  102. rfc850_year(Y) when Y > 50 -> Y + 1900;
  103. rfc850_year(Y) -> Y + 2000.
  104. asctime_date(<<"Jan ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  105. {{?DIGITS(Y1, Y2, Y3, Y4), 1, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  106. asctime_date(<<"Feb ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  107. {{?DIGITS(Y1, Y2, Y3, Y4), 2, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  108. asctime_date(<<"Mar ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  109. {{?DIGITS(Y1, Y2, Y3, Y4), 3, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  110. asctime_date(<<"Apr ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  111. {{?DIGITS(Y1, Y2, Y3, Y4), 4, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  112. asctime_date(<<"May ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  113. {{?DIGITS(Y1, Y2, Y3, Y4), 5, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  114. asctime_date(<<"Jun ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  115. {{?DIGITS(Y1, Y2, Y3, Y4), 6, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  116. asctime_date(<<"Jul ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  117. {{?DIGITS(Y1, Y2, Y3, Y4), 7, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  118. asctime_date(<<"Aug ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  119. {{?DIGITS(Y1, Y2, Y3, Y4), 8, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  120. asctime_date(<<"Sep ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  121. {{?DIGITS(Y1, Y2, Y3, Y4), 9, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  122. asctime_date(<<"Oct ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  123. {{?DIGITS(Y1, Y2, Y3, Y4), 10, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  124. asctime_date(<<"Nov ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  125. {{?DIGITS(Y1, Y2, Y3, Y4), 11, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}};
  126. asctime_date(<<"Dec ", D1, D2, " ", H1, H2, ":", M1, M2, ":", S1, S2, " ", Y1, Y2, Y3, Y4 >>) ->
  127. {{?DIGITS(Y1, Y2, Y3, Y4), 12, asctime_day(D1, D2)}, {?DIGITS(H1, H2), ?DIGITS(M1, M2), ?DIGITS(S1, S2)}}.
  128. asctime_day($\s, D2) -> (D2 - $0);
  129. asctime_day(D1, D2) -> (D1 - $0) * 10 + (D2 - $0).
  130. -ifdef(TEST).
  131. day_name() -> oneof(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]).
  132. day_name_l() -> oneof(["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]).
  133. year() -> integer(1951, 2050).
  134. month() -> integer(1, 12).
  135. day() -> integer(1, 31).
  136. hour() -> integer(0, 23).
  137. minute() -> integer(0, 59).
  138. second() -> integer(0, 60).
  139. fixdate_gen() ->
  140. ?LET({DayName, Y, Mo, D, H, Mi, S},
  141. {day_name(), year(), month(), day(), hour(), minute(), second()},
  142. {{{Y, Mo, D}, {H, Mi, S}},
  143. list_to_binary([DayName, ", ", pad_int(D), " ", month(Mo), " ", integer_to_binary(Y),
  144. " ", pad_int(H), ":", pad_int(Mi), ":", pad_int(S), " GMT"])}).
  145. rfc850_gen() ->
  146. ?LET({DayName, Y, Mo, D, H, Mi, S},
  147. {day_name_l(), year(), month(), day(), hour(), minute(), second()},
  148. {{{Y, Mo, D}, {H, Mi, S}},
  149. list_to_binary([DayName, ", ", pad_int(D), "-", month(Mo), "-", pad_int(Y rem 100),
  150. " ", pad_int(H), ":", pad_int(Mi), ":", pad_int(S), " GMT"])}).
  151. asctime_gen() ->
  152. ?LET({DayName, Y, Mo, D, H, Mi, S},
  153. {day_name(), year(), month(), day(), hour(), minute(), second()},
  154. {{{Y, Mo, D}, {H, Mi, S}},
  155. list_to_binary([DayName, " ", month(Mo), " ",
  156. if D < 10 -> << $\s, (D + $0) >>; true -> integer_to_binary(D) end,
  157. " ", pad_int(H), ":", pad_int(Mi), ":", pad_int(S), " ", integer_to_binary(Y)])}).
  158. prop_http_date() ->
  159. ?FORALL({Date, DateBin},
  160. oneof([fixdate_gen(), rfc850_gen(), asctime_gen()]),
  161. Date =:= parse_date(DateBin)).
  162. http_date_test_() ->
  163. Tests = [
  164. {<<"Sun, 06 Nov 1994 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  165. {<<"Sunday, 06-Nov-94 08:49:37 GMT">>, {{1994, 11, 6}, {8, 49, 37}}},
  166. {<<"Sun Nov 6 08:49:37 1994">>, {{1994, 11, 6}, {8, 49, 37}}}
  167. ],
  168. [{V, fun() -> R = http_date(V) end} || {V, R} <- Tests].
  169. horse_http_date_fixdate() ->
  170. horse:repeat(200000,
  171. http_date(<<"Sun, 06 Nov 1994 08:49:37 GMT">>)
  172. ).
  173. horse_http_date_rfc850() ->
  174. horse:repeat(200000,
  175. http_date(<<"Sunday, 06-Nov-94 08:49:37 GMT">>)
  176. ).
  177. horse_http_date_asctime() ->
  178. horse:repeat(200000,
  179. http_date(<<"Sun Nov 6 08:49:37 1994">>)
  180. ).
  181. -endif.
  182. %% @doc Return the date formatted according to RFC1123.
  183. -spec rfc1123(calendar:datetime()) -> binary().
  184. rfc1123(DateTime) ->
  185. rfc7231(DateTime).
  186. %% @doc Return the date formatted according to RFC2109.
  187. -spec rfc2109(calendar:datetime()) -> binary().
  188. rfc2109({Date = {Y, Mo, D}, {H, Mi, S}}) ->
  189. Wday = calendar:day_of_the_week(Date),
  190. << (weekday(Wday))/binary, ", ",
  191. (pad_int(D))/binary, "-",
  192. (month(Mo))/binary, "-",
  193. (year(Y))/binary, " ",
  194. (pad_int(H))/binary, ":",
  195. (pad_int(Mi))/binary, ":",
  196. (pad_int(S))/binary, " GMT" >>.
  197. -ifdef(TEST).
  198. rfc2109_test_() ->
  199. Tests = [
  200. {<<"Sat, 14-May-2011 14:25:33 GMT">>, {{2011, 5, 14}, {14, 25, 33}}},
  201. {<<"Sun, 01-Jan-2012 00:00:00 GMT">>, {{2012, 1, 1}, { 0, 0, 0}}}
  202. ],
  203. [{R, fun() -> R = rfc2109(D) end} || {R, D} <- Tests].
  204. horse_rfc2109_20130101_000000() ->
  205. horse:repeat(100000,
  206. rfc2109({{2013, 1, 1}, {0, 0, 0}})
  207. ).
  208. horse_rfc2109_20131231_235959() ->
  209. horse:repeat(100000,
  210. rfc2109({{2013, 12, 31}, {23, 59, 59}})
  211. ).
  212. horse_rfc2109_12340506_070809() ->
  213. horse:repeat(100000,
  214. rfc2109({{1234, 5, 6}, {7, 8, 9}})
  215. ).
  216. -endif.
  217. %% @doc Return the date formatted according to RFC7231.
  218. -spec rfc7231(calendar:datetime()) -> binary().
  219. rfc7231({Date = {Y, Mo, D}, {H, Mi, S}}) ->
  220. Wday = calendar:day_of_the_week(Date),
  221. << (weekday(Wday))/binary, ", ",
  222. (pad_int(D))/binary, " ",
  223. (month(Mo))/binary, " ",
  224. (year(Y))/binary, " ",
  225. (pad_int(H))/binary, ":",
  226. (pad_int(Mi))/binary, ":",
  227. (pad_int(S))/binary, " GMT" >>.
  228. -ifdef(TEST).
  229. rfc7231_test_() ->
  230. Tests = [
  231. {<<"Sat, 14 May 2011 14:25:33 GMT">>, {{2011, 5, 14}, {14, 25, 33}}},
  232. {<<"Sun, 01 Jan 2012 00:00:00 GMT">>, {{2012, 1, 1}, { 0, 0, 0}}}
  233. ],
  234. [{R, fun() -> R = rfc7231(D) end} || {R, D} <- Tests].
  235. horse_rfc7231_20130101_000000() ->
  236. horse:repeat(100000,
  237. rfc7231({{2013, 1, 1}, {0, 0, 0}})
  238. ).
  239. horse_rfc7231_20131231_235959() ->
  240. horse:repeat(100000,
  241. rfc7231({{2013, 12, 31}, {23, 59, 59}})
  242. ).
  243. horse_rfc7231_12340506_070809() ->
  244. horse:repeat(100000,
  245. rfc7231({{1234, 5, 6}, {7, 8, 9}})
  246. ).
  247. -endif.
  248. %% Internal.
  249. -spec pad_int(0..59) -> <<_:16>>.
  250. pad_int( 0) -> <<"00">>;
  251. pad_int( 1) -> <<"01">>;
  252. pad_int( 2) -> <<"02">>;
  253. pad_int( 3) -> <<"03">>;
  254. pad_int( 4) -> <<"04">>;
  255. pad_int( 5) -> <<"05">>;
  256. pad_int( 6) -> <<"06">>;
  257. pad_int( 7) -> <<"07">>;
  258. pad_int( 8) -> <<"08">>;
  259. pad_int( 9) -> <<"09">>;
  260. pad_int(10) -> <<"10">>;
  261. pad_int(11) -> <<"11">>;
  262. pad_int(12) -> <<"12">>;
  263. pad_int(13) -> <<"13">>;
  264. pad_int(14) -> <<"14">>;
  265. pad_int(15) -> <<"15">>;
  266. pad_int(16) -> <<"16">>;
  267. pad_int(17) -> <<"17">>;
  268. pad_int(18) -> <<"18">>;
  269. pad_int(19) -> <<"19">>;
  270. pad_int(20) -> <<"20">>;
  271. pad_int(21) -> <<"21">>;
  272. pad_int(22) -> <<"22">>;
  273. pad_int(23) -> <<"23">>;
  274. pad_int(24) -> <<"24">>;
  275. pad_int(25) -> <<"25">>;
  276. pad_int(26) -> <<"26">>;
  277. pad_int(27) -> <<"27">>;
  278. pad_int(28) -> <<"28">>;
  279. pad_int(29) -> <<"29">>;
  280. pad_int(30) -> <<"30">>;
  281. pad_int(31) -> <<"31">>;
  282. pad_int(32) -> <<"32">>;
  283. pad_int(33) -> <<"33">>;
  284. pad_int(34) -> <<"34">>;
  285. pad_int(35) -> <<"35">>;
  286. pad_int(36) -> <<"36">>;
  287. pad_int(37) -> <<"37">>;
  288. pad_int(38) -> <<"38">>;
  289. pad_int(39) -> <<"39">>;
  290. pad_int(40) -> <<"40">>;
  291. pad_int(41) -> <<"41">>;
  292. pad_int(42) -> <<"42">>;
  293. pad_int(43) -> <<"43">>;
  294. pad_int(44) -> <<"44">>;
  295. pad_int(45) -> <<"45">>;
  296. pad_int(46) -> <<"46">>;
  297. pad_int(47) -> <<"47">>;
  298. pad_int(48) -> <<"48">>;
  299. pad_int(49) -> <<"49">>;
  300. pad_int(50) -> <<"50">>;
  301. pad_int(51) -> <<"51">>;
  302. pad_int(52) -> <<"52">>;
  303. pad_int(53) -> <<"53">>;
  304. pad_int(54) -> <<"54">>;
  305. pad_int(55) -> <<"55">>;
  306. pad_int(56) -> <<"56">>;
  307. pad_int(57) -> <<"57">>;
  308. pad_int(58) -> <<"58">>;
  309. pad_int(59) -> <<"59">>;
  310. pad_int(60) -> <<"60">>;
  311. pad_int(Int) -> integer_to_binary(Int).
  312. -spec weekday(1..7) -> <<_:24>>.
  313. weekday(1) -> <<"Mon">>;
  314. weekday(2) -> <<"Tue">>;
  315. weekday(3) -> <<"Wed">>;
  316. weekday(4) -> <<"Thu">>;
  317. weekday(5) -> <<"Fri">>;
  318. weekday(6) -> <<"Sat">>;
  319. weekday(7) -> <<"Sun">>.
  320. -spec month(1..12) -> <<_:24>>.
  321. month( 1) -> <<"Jan">>;
  322. month( 2) -> <<"Feb">>;
  323. month( 3) -> <<"Mar">>;
  324. month( 4) -> <<"Apr">>;
  325. month( 5) -> <<"May">>;
  326. month( 6) -> <<"Jun">>;
  327. month( 7) -> <<"Jul">>;
  328. month( 8) -> <<"Aug">>;
  329. month( 9) -> <<"Sep">>;
  330. month(10) -> <<"Oct">>;
  331. month(11) -> <<"Nov">>;
  332. month(12) -> <<"Dec">>.
  333. -spec year(pos_integer()) -> <<_:32>>.
  334. year(1970) -> <<"1970">>;
  335. year(1971) -> <<"1971">>;
  336. year(1972) -> <<"1972">>;
  337. year(1973) -> <<"1973">>;
  338. year(1974) -> <<"1974">>;
  339. year(1975) -> <<"1975">>;
  340. year(1976) -> <<"1976">>;
  341. year(1977) -> <<"1977">>;
  342. year(1978) -> <<"1978">>;
  343. year(1979) -> <<"1979">>;
  344. year(1980) -> <<"1980">>;
  345. year(1981) -> <<"1981">>;
  346. year(1982) -> <<"1982">>;
  347. year(1983) -> <<"1983">>;
  348. year(1984) -> <<"1984">>;
  349. year(1985) -> <<"1985">>;
  350. year(1986) -> <<"1986">>;
  351. year(1987) -> <<"1987">>;
  352. year(1988) -> <<"1988">>;
  353. year(1989) -> <<"1989">>;
  354. year(1990) -> <<"1990">>;
  355. year(1991) -> <<"1991">>;
  356. year(1992) -> <<"1992">>;
  357. year(1993) -> <<"1993">>;
  358. year(1994) -> <<"1994">>;
  359. year(1995) -> <<"1995">>;
  360. year(1996) -> <<"1996">>;
  361. year(1997) -> <<"1997">>;
  362. year(1998) -> <<"1998">>;
  363. year(1999) -> <<"1999">>;
  364. year(2000) -> <<"2000">>;
  365. year(2001) -> <<"2001">>;
  366. year(2002) -> <<"2002">>;
  367. year(2003) -> <<"2003">>;
  368. year(2004) -> <<"2004">>;
  369. year(2005) -> <<"2005">>;
  370. year(2006) -> <<"2006">>;
  371. year(2007) -> <<"2007">>;
  372. year(2008) -> <<"2008">>;
  373. year(2009) -> <<"2009">>;
  374. year(2010) -> <<"2010">>;
  375. year(2011) -> <<"2011">>;
  376. year(2012) -> <<"2012">>;
  377. year(2013) -> <<"2013">>;
  378. year(2014) -> <<"2014">>;
  379. year(2015) -> <<"2015">>;
  380. year(2016) -> <<"2016">>;
  381. year(2017) -> <<"2017">>;
  382. year(2018) -> <<"2018">>;
  383. year(2019) -> <<"2019">>;
  384. year(2020) -> <<"2020">>;
  385. year(2021) -> <<"2021">>;
  386. year(2022) -> <<"2022">>;
  387. year(2023) -> <<"2023">>;
  388. year(2024) -> <<"2024">>;
  389. year(2025) -> <<"2025">>;
  390. year(2026) -> <<"2026">>;
  391. year(2027) -> <<"2027">>;
  392. year(2028) -> <<"2028">>;
  393. year(2029) -> <<"2029">>;
  394. year(Year) -> integer_to_binary(Year).