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