mirror of
https://github.com/valitydev/rfc3339.git
synced 2024-11-06 00:25:16 +00:00
restrict input/output to rfc3339 spec
This commit is contained in:
parent
373bc76f2e
commit
64e531dbfa
@ -5,13 +5,14 @@ defmodule RFC3339 do
|
||||
|
||||
def parse(dt) do
|
||||
case :rfc3339.parse(dt) do
|
||||
{:ok, {date, time, tz}} ->
|
||||
{:ok, {date, time, usec, tz}} ->
|
||||
dt = %RFC3339{}
|
||||
dt = insert_date(dt, date)
|
||||
dt = insert_time(dt, time)
|
||||
dt = insert_usec(dt, usec)
|
||||
insert_tz(dt, tz)
|
||||
{:error, error} ->
|
||||
RFC3339.Error.from_erl(error)
|
||||
{:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
@ -20,26 +21,15 @@ defmodule RFC3339 do
|
||||
defp insert_date(dt, {year, month, day}) do
|
||||
%{ dt | :year => year, :month => month, :day => day }
|
||||
end
|
||||
defp insert_date(dt, :undefined), do: dt
|
||||
|
||||
defp insert_time(dt, {hour, min, sec, :undefined}) do
|
||||
defp insert_time(dt, {hour, min, sec}) do
|
||||
%{ dt | :hour => hour, :min => min, :sec => sec }
|
||||
end
|
||||
defp insert_time(dt, {hour, min, sec, usec}) do
|
||||
%{ dt | :hour => hour, :min => min, :sec => sec, :usec => usec }
|
||||
end
|
||||
defp insert_time(dt, :undefined), do: dt
|
||||
|
||||
defp insert_usec(dt, :undefined), do: dt
|
||||
defp insert_usec(dt, usec), do: %{ dt | :usec => usec }
|
||||
|
||||
defp insert_tz(dt, :undefined), do: dt
|
||||
defp insert_tz(dt, offset), do: %{ dt | :tz_offset => offset }
|
||||
end
|
||||
|
||||
defmodule RFC3339.Error do
|
||||
defstruct message: nil, reason: nil
|
||||
|
||||
def from_erl(error) do
|
||||
%RFC3339.Error{message: error_to_message(error), reason: error}
|
||||
end
|
||||
|
||||
defp error_to_message(_), do: ""
|
||||
end
|
||||
|
263
src/rfc3339.erl
263
src/rfc3339.erl
@ -2,169 +2,146 @@
|
||||
|
||||
-export([parse/1]).
|
||||
-export([format/1]).
|
||||
-export([to_tuple/1]).
|
||||
-export([to_map/1]).
|
||||
-export_type([datetime/0, date/0, time/0, tz/0]).
|
||||
-export_type([year/0, month/0, day/0]).
|
||||
-export_type([hour/0, min/0, sec/0, usec/0]).
|
||||
-export_type([tz_offset/0]).
|
||||
-export_type([(error/0)]).
|
||||
|
||||
-type datetime() :: {date(), time(), tz()}.
|
||||
-type datetime() :: {date(), time()}.
|
||||
|
||||
-type date() :: {year(), month(), day()}.
|
||||
-type year() :: 0..9999.
|
||||
-type month() :: 0..12.
|
||||
-type day() :: 0..31.
|
||||
-type month() :: 1..12.
|
||||
-type day() :: 1..31.
|
||||
|
||||
-type time() :: {hour(), min(), sec(), usec()}.
|
||||
-type time() :: {hour(), min(), sec()}.
|
||||
-type hour() :: 0..24.
|
||||
-type min() :: 0..59.
|
||||
-type sec() :: 0..60.
|
||||
|
||||
-type usec() :: 0..999999.
|
||||
|
||||
-type tz() :: tz_offset().
|
||||
-type tz_offset() :: -1439..1439.
|
||||
|
||||
-type error() :: map().
|
||||
-type error() :: badarg | baddate | badtime | badyear | badday | badhour | badminute | badsecond | badusec | badtimezone.
|
||||
|
||||
|
||||
-spec parse(binary()) -> {ok, datetime()} | {error, error()}.
|
||||
parse(Bin) -> time_or_date(Bin, {undefined, undefined, undefined}).
|
||||
|
||||
-spec format(map() | datetime()) -> {ok, binary()} | {error, error()}.
|
||||
format({{Year, Month, Day}, {Hour, Min, Sec, USec}, Offset}) ->
|
||||
DT = #{year => Year, month => Month, day => Day,
|
||||
hour => Hour, min => Min, sec => Sec, usec => USec,
|
||||
tz_offset => Offset},
|
||||
format(DT);
|
||||
format(DT) ->
|
||||
Date = format_date(DT),
|
||||
Time = format_time(DT),
|
||||
format(Date, Time).
|
||||
|
||||
-spec to_tuple(binary()) -> {ok, datetime()} | {error, error()}.
|
||||
to_tuple(Bin) -> parse(Bin).
|
||||
-spec parse(binary()) -> {ok, {date(), time(), usec(), tz()}} | {error, error()}.
|
||||
parse(Bin) when is_binary(Bin) -> date(Bin, {undefined, undefined, undefined, undefined});
|
||||
parse(_) -> {error, badarg}.
|
||||
|
||||
-spec to_map(binary()) -> {ok, map()} | {error, error()}.
|
||||
to_map(Bin) ->
|
||||
to_map(Bin) when is_binary(Bin) ->
|
||||
case parse(Bin) of
|
||||
{ok, {Date, Time, Offset}} ->
|
||||
mapify(Date, Time, Offset, #{});
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
{ok, {Date, Time, USec, Tz}} -> mapify(Date, Time, USec, Tz, #{});
|
||||
{error, Error} -> {error, Error}
|
||||
end;
|
||||
to_map(_) -> {error, badarg}.
|
||||
|
||||
mapify(undefined, Time, Offset, Result) -> mapify(Time, Offset, Result);
|
||||
mapify({Year, Month, Day}, Time, Offset, Result) ->
|
||||
mapify(Time, Offset, maps:merge(Result, #{year => Year, month => Month, day => Day})).
|
||||
mapify({Year, Month, Day}, Time, USec, Tz, Result)
|
||||
when is_integer(Year), is_integer(Month), is_integer(Day) ->
|
||||
mapify(Time, USec, Tz, maps:merge(Result, #{year => Year, month => Month, day => Day}));
|
||||
mapify(_, _, _, _, _) -> {error, badarg}.
|
||||
|
||||
mapify(undefined, _, Result) -> Result;
|
||||
mapify({Hour, Min, Sec, undefined}, Offset, Result) ->
|
||||
mapify(Offset, maps:merge(Result, #{hour => Hour, min => Min, sec => Sec}));
|
||||
mapify({Hour, Min, Sec, USec}, Offset, Result) ->
|
||||
mapify(Offset, maps:merge(Result, #{hour => Hour, min => Min, sec => Sec, usec => USec})).
|
||||
mapify({Hour, Min, Sec}, USec, Tz, Result)
|
||||
when is_integer(Hour), is_integer(Min), is_integer(Sec) ->
|
||||
mapify(USec, Tz, maps:merge(Result, #{hour => Hour, min => Min, sec => Sec}));
|
||||
mapify(_, _, _, _) -> {error, badarg}.
|
||||
|
||||
mapify(undefined, Tz, Result) -> mapify(Tz, Result);
|
||||
mapify(USec, Tz, Result) when is_integer(USec) ->
|
||||
mapify(Tz, maps:merge(Result, #{usec => USec}));
|
||||
mapify(_, _, _) -> {error, badarg}.
|
||||
|
||||
mapify(undefined, Result) -> Result;
|
||||
mapify(Offset, Result) ->
|
||||
maps:merge(Result, #{tz_offset => Offset}).
|
||||
mapify(Tz, Result) when is_integer(Tz) -> maps:merge(Result, #{tz_offset => Tz});
|
||||
mapify(_, _) -> {error, badarg}.
|
||||
|
||||
time_or_date(<<H1, H2, $:, M1, M2, $:, S1, S2, Rest/binary>>, _Result) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
Sec = to_second(S1, S2),
|
||||
usec_or_tz(Rest, {undefined, {Hour, Min, Sec, undefined}, undefined});
|
||||
time_or_date(<<Y1, Y2, Y3, Y4, $-, M1, M2, $-, D1, D2, Rest/binary>>, _Result) ->
|
||||
|
||||
-spec format(map() | {date(), time(), usec(), tz()}) -> {ok, binary()} | {error, error()}.
|
||||
format({Date, Time, USec, Tz})
|
||||
when is_tuple(Date), is_tuple(Time) ->
|
||||
format(mapify(Date, Time, USec, Tz, #{}));
|
||||
format(Dt) when is_map(Dt) ->
|
||||
Date = format_date(Dt),
|
||||
Time = format_time(Dt),
|
||||
format_(Date, Time).
|
||||
|
||||
|
||||
date(<<Y1, Y2, Y3, Y4, $-, M1, M2, $-, D1, D2, Rest/binary>>, {undefined, undefined, undefined, undefined}) ->
|
||||
Year = to_year(Y1, Y2, Y3, Y4),
|
||||
Month = to_month(M1, M2),
|
||||
Day = to_day(D1, D2, Year, Month),
|
||||
time_or_end(Rest, {{Year, Month, Day}, undefined, undefined});
|
||||
time_or_date(_, _Result) ->
|
||||
{error, badarg}.
|
||||
Date = coalesce(Year, Month, Day),
|
||||
time(Rest, {Date, undefined, undefined, undefined});
|
||||
date(_, _Result) ->
|
||||
{error, baddate}.
|
||||
|
||||
%% space
|
||||
time(_, {{error, Error}, _, _, _}) -> {error, Error};
|
||||
time(<<Sep, H1, H2, $:, M1, M2, $:, S1, S2, Rest/binary>>, {Date, undefined, undefined, undefined})
|
||||
when Sep == 16#20 orelse Sep == $t orelse Sep == $T ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
Sec = to_second(S1, S2),
|
||||
Time = coalesce(Hour, Min, Sec),
|
||||
usec_or_tz(Rest, {Date, Time, undefined, undefined});
|
||||
time(_, _) -> {error, badtime}.
|
||||
|
||||
usec_or_tz(_, {_, {error, Error}, _, _}) -> {error, Error};
|
||||
usec_or_tz(<<$., Rest/binary>>, Result) ->
|
||||
usec(Rest, Result, 0, 100000);
|
||||
usec_or_tz(Rest, Result) -> tz(Rest, Result).
|
||||
|
||||
%% next two clauses burn off fractional seconds beyond microsecond precision
|
||||
usec(<<X, Rest/binary>>, Result, undefined, undefined)
|
||||
usec(<<X, Rest/binary>>, Result, USec, 0)
|
||||
when X >= $0 andalso X =< $9 ->
|
||||
usec(Rest, Result, undefined, undefined);
|
||||
usec(Bin, Result, undefined, undefined) ->
|
||||
tz(Bin, Result);
|
||||
usec(Rest, Result, USec, 0);
|
||||
%% keep a running acc of usecs
|
||||
usec(<<X, Rest/binary>>, Result, Acc, Multiplier)
|
||||
usec(<<X, Rest/binary>>, Result, USec, Multiplier)
|
||||
when X >= $0 andalso X =< $9 ->
|
||||
try list_to_integer([X]) of
|
||||
N -> usec(Rest, Result, Acc + (N * Multiplier), Multiplier div 10)
|
||||
N -> usec(Rest, Result, USec + (N * Multiplier), Multiplier div 10)
|
||||
catch
|
||||
error:badarg -> {error, badusec}
|
||||
end;
|
||||
%% not a digit, insert usecs into time and proceed to tz
|
||||
usec(Bin, {Date, {Hour, Min, Sec, undefined}, undefined}, Acc, _) ->
|
||||
tz(Bin, {Date, {Hour, Min, Sec, Acc}, undefined});
|
||||
usec(Bin, {Date, Time, undefined, undefined}, USec, _) ->
|
||||
tz(Bin, {Date, Time, USec, undefined});
|
||||
usec(_, _, _, _) -> {error, badusec}.
|
||||
|
||||
tz(<<$+, H1, H2, $:, M1, M2>>, {Date, Time, undefined}) ->
|
||||
tz(<<$+, H1, H2, $:, M1, M2>>, {Date, Time, USec, undefined}) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
{ok, {Date, Time, (Hour * 60) + Min}};
|
||||
tz(<<$-, H1, H2, $:, M1, M2>>, {Date, Time, undefined}) ->
|
||||
case calc_tz(positive, Hour, Min) of
|
||||
TZ when is_integer(TZ) -> {ok, {Date, Time, USec, TZ}};
|
||||
{error, Error} -> {error, Error}
|
||||
end;
|
||||
tz(<<$-, H1, H2, $:, M1, M2>>, {Date, Time, USec, undefined}) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
{ok, {Date, Time, (Hour * -60) - Min}};
|
||||
case calc_tz(negative, Hour, Min) of
|
||||
TZ when is_integer(TZ) -> {ok, {Date, Time, USec, TZ}};
|
||||
{error, Error} -> {error, Error}
|
||||
end;
|
||||
tz(<<$Z>>, Result) ->
|
||||
{ok, Result};
|
||||
tz(<<$z>>, Result) ->
|
||||
{ok, Result};
|
||||
tz(_, _) -> {error, badtimezone}.
|
||||
|
||||
%% space
|
||||
time_or_end(<<16#20, H1, H2, $:, M1, M2, $:, S1, S2, Rest/binary>>, {Date, undefined, undefined}) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
Sec = to_second(S1, S2),
|
||||
usec_or_tz(Rest, {Date, {Hour, Min, Sec, undefined}, undefined});
|
||||
time_or_end(<<$t, H1, H2, $:, M1, M2, $:, S1, S2, Rest/binary>>, {Date, undefined, undefined}) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
Sec = to_second(S1, S2),
|
||||
usec_or_tz(Rest, {Date, {Hour, Min, Sec, undefined}, undefined});
|
||||
time_or_end(<<$T, H1, H2, $:, M1, M2, $:, S1, S2, Rest/binary>>, {Date, undefined, undefined}) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
Min = to_minute(M1, M2),
|
||||
Sec = to_second(S1, S2),
|
||||
usec_or_tz(Rest, {Date, {Hour, Min, Sec, undefined}, undefined});
|
||||
time_or_end(<<>>, Result) -> {ok, Result};
|
||||
time_or_end(_, _) -> {error, badtime}.
|
||||
calc_tz(_, {error, Error}, _) -> {error, Error};
|
||||
calc_tz(_, _, {error, Error}) -> {error, Error};
|
||||
calc_tz(positive, Hour, Min) -> (Hour * 60) + Min;
|
||||
calc_tz(negative, Hour, Min) -> (Hour * -60) - Min.
|
||||
|
||||
to_hour(H1, H2) ->
|
||||
try list_to_integer([H1, H2]) of
|
||||
X when X >= 0 andalso X =< 23 -> X;
|
||||
_ -> {error, badhour}
|
||||
catch
|
||||
error:badarg -> {error, badhour}
|
||||
end.
|
||||
|
||||
to_minute(M1, M2) ->
|
||||
try list_to_integer([M1, M2]) of
|
||||
X when X >= 0 andalso X =< 59 -> X;
|
||||
_ -> {error, badminute}
|
||||
catch
|
||||
error:badarg -> {error, badminute}
|
||||
end.
|
||||
|
||||
to_second(S1, S2) ->
|
||||
try list_to_integer([S1, S2]) of
|
||||
X when X >= 0 andalso X =< 60 -> X;
|
||||
_ -> {error, badsecond}
|
||||
catch
|
||||
error:badarg -> {error, badsecond}
|
||||
end.
|
||||
coalesce({error, Error}, _, _) -> {error, Error};
|
||||
coalesce(_, {error, Error}, _) -> {error, Error};
|
||||
coalesce(_, _, {error, Error}) -> {error, Error};
|
||||
coalesce(X, Y, Z) -> {X, Y, Z}.
|
||||
|
||||
to_year(Y1, Y2, Y3, Y4) ->
|
||||
try list_to_integer([Y1, Y2, Y3, Y4]) of
|
||||
X when X >= 0 andalso X =< 9999 -> X;
|
||||
Year when Year >= 0 andalso Year =< 9999 -> Year;
|
||||
_ -> {error, badyear}
|
||||
catch
|
||||
error:badarg -> {error, badyear}
|
||||
@ -172,7 +149,7 @@ to_year(Y1, Y2, Y3, Y4) ->
|
||||
|
||||
to_month(M1, M2) ->
|
||||
try list_to_integer([M1, M2]) of
|
||||
X when X >= 1 andalso X =< 12 -> X;
|
||||
Month when Month >= 1 andalso Month =< 12 -> Month;
|
||||
_ -> {error, badmonth}
|
||||
catch
|
||||
error:badarg -> {error, badmonth}
|
||||
@ -180,10 +157,10 @@ to_month(M1, M2) ->
|
||||
|
||||
to_day(D1, D2, Year, Month) ->
|
||||
try list_to_integer([D1, D2]) of
|
||||
X when X >= 0 andalso X =< 28 -> X;
|
||||
X when X >= 29 andalso X =< 31 ->
|
||||
case day_in_month(Year, Month, X) of
|
||||
true -> X;
|
||||
Day when Day >= 0 andalso Day =< 28 -> Day;
|
||||
Day when Day >= 29 andalso Day =< 31 ->
|
||||
case day_in_month(Year, Month, Day) of
|
||||
true -> Day;
|
||||
false -> {error, badday}
|
||||
end;
|
||||
_ -> {error, badday}
|
||||
@ -201,37 +178,55 @@ day_in_month(Year, Month, Day) ->
|
||||
_ -> true
|
||||
end.
|
||||
|
||||
format_date(DT) ->
|
||||
Year = maps:get(year, DT, undefined),
|
||||
Month = maps:get(month, DT, undefined),
|
||||
Day = maps:get(day, DT, undefined),
|
||||
to_hour(H1, H2) ->
|
||||
try list_to_integer([H1, H2]) of
|
||||
Hour when Hour >= 0 andalso Hour =< 23 -> Hour;
|
||||
_ -> {error, badhour}
|
||||
catch
|
||||
error:badarg -> {error, badhour}
|
||||
end.
|
||||
|
||||
to_minute(M1, M2) ->
|
||||
try list_to_integer([M1, M2]) of
|
||||
Min when Min >= 0 andalso Min =< 59 -> Min;
|
||||
_ -> {error, badminute}
|
||||
catch
|
||||
error:badarg -> {error, badminute}
|
||||
end.
|
||||
|
||||
to_second(S1, S2) ->
|
||||
try list_to_integer([S1, S2]) of
|
||||
Sec when Sec >= 0 andalso Sec =< 60 -> Sec;
|
||||
_ -> {error, badsecond}
|
||||
catch
|
||||
error:badarg -> {error, badsecond}
|
||||
end.
|
||||
|
||||
format_date(Dt) ->
|
||||
Year = g(year, Dt),
|
||||
Month = g(month, Dt),
|
||||
Day = g(day, Dt),
|
||||
format_date(Year, Month, Day).
|
||||
|
||||
format_date(Y, M, D) when is_integer(Y), is_integer(M), is_integer(D) ->
|
||||
io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B", [Y, M, D]);
|
||||
format_date(undefined, undefined, undefined) -> {error, nodate};
|
||||
format_date(nil, nil, nil) -> {error, nodate};
|
||||
format_date(_, _, _) -> {error, baddate}.
|
||||
|
||||
format_time(DT) ->
|
||||
Hour = maps:get(hour, DT, undefined),
|
||||
Min = maps:get(min, DT, undefined),
|
||||
Sec = maps:get(sec, DT, undefined),
|
||||
USec = maps:get(usec, DT, undefined),
|
||||
format_time(Dt) ->
|
||||
Hour = g(hour, Dt),
|
||||
Min = g(min, Dt),
|
||||
Sec = g(sec, Dt),
|
||||
USec = g(usec, Dt),
|
||||
Time = format_time(Hour, Min, Sec, USec),
|
||||
Offset = maps:get(tz_offset, DT, undefined),
|
||||
Offset = g(tz_offset, Dt),
|
||||
TZ = format_offset(Offset),
|
||||
format_time(Time, TZ).
|
||||
|
||||
format_time(H, M, S, undefined) when is_integer(H), is_integer(M), is_integer(S) ->
|
||||
io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [H, M, S]);
|
||||
format_time(H, M, S, nil) when is_integer(H), is_integer(M), is_integer(S) ->
|
||||
format_time(H, M, S, 0) when is_integer(H), is_integer(M), is_integer(S) ->
|
||||
io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [H, M, S]);
|
||||
format_time(H, M, S, U) when is_integer(H), is_integer(M), is_integer(S), is_integer(U) ->
|
||||
SU = (S / 1) + (U / 1000000),
|
||||
io_lib:format("~2.10.0B:~2.10.0B:~9.6.0f", [H, M, SU]);
|
||||
format_time(undefined, undefined, undefined, undefined) -> {error, notime};
|
||||
format_time(nil, nil, nil, nil) -> {error, notime};
|
||||
format_time(_, _, _, _) -> {error, badtime}.
|
||||
|
||||
format_offset(undefined) -> "Z";
|
||||
@ -246,9 +241,13 @@ format_offset(M) when is_integer(M) ->
|
||||
format_time({error, Error}, _) -> {error, Error};
|
||||
format_time(Time, TZ) -> [Time, TZ].
|
||||
|
||||
format({error, baddate}, _) -> {error, badarg};
|
||||
format(_, {error, badtime}) -> {error, badtime};
|
||||
format({error, nodate}, {error, notime}) -> {error, badarg};
|
||||
format({error, nodate}, Time) -> unicode:characters_to_binary(Time);
|
||||
format(Date, {error, notime}) -> unicode:characters_to_binary(Date);
|
||||
format(Date, Time) -> unicode:characters_to_binary([Date, "T", Time]).
|
||||
format_({error, baddate}, _) -> {error, badarg};
|
||||
format_(_, {error, badtime}) -> {error, badtime};
|
||||
format_(Date, Time) -> unicode:characters_to_binary([Date, "T", Time]).
|
||||
|
||||
g(Key, Map) ->
|
||||
case maps:get(Key, Map, undefined) of
|
||||
nil -> 0;
|
||||
undefined -> 0;
|
||||
Val -> Val
|
||||
end.
|
@ -1,132 +1,325 @@
|
||||
defmodule RFC3339Test do
|
||||
defmodule :rfc3339_test do
|
||||
use ExUnit.Case
|
||||
|
||||
test "decode 0000-01-01" do
|
||||
assert %RFC3339{year: 0, month: 1, day: 1} == RFC3339.parse("0000-01-01")
|
||||
test "decode 0000-01-01T00:00:00.0+00:00" do
|
||||
assert {:ok, {{0, 1, 1}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("0000-01-01T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 9999-12-31" do
|
||||
assert %RFC3339{year: 9999, month: 12, day: 31} == RFC3339.parse("9999-12-31")
|
||||
test "decode 9999-12-31T00:00:00.0+00:00" do
|
||||
assert {:ok, {{9999, 12, 31}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("9999-12-31T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 1584-03-04" do
|
||||
assert %RFC3339{year: 1584, month: 3, day: 4} == RFC3339.parse("1584-03-04")
|
||||
test "decode 1584-03-04T00:00:00.0+00:00" do
|
||||
assert {:ok, {{1584, 3, 4}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("1584-03-04T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 1900-01-01" do
|
||||
assert %RFC3339{year: 1900, month: 1, day: 1} == RFC3339.parse("1900-01-01")
|
||||
test "decode 1900-01-01T00:00:00.0+00:00" do
|
||||
assert {:ok, {{1900, 1, 1}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("1900-01-01T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 2016-01-24" do
|
||||
assert %RFC3339{year: 2016, month: 1, day: 24} == RFC3339.parse("2016-01-24")
|
||||
test "decode 2016-01-24T00:00:00.0+00:00" do
|
||||
assert {:ok, {{2016, 1, 24}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("2016-01-24T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 00:00:00Z" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0} == RFC3339.parse("00:00:00Z")
|
||||
test "decode 1970-01-01T00:00:00Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, :undefined, :undefined}} = :rfc3339.parse("1970-01-01T00:00:00Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60} == RFC3339.parse("23:59:60Z")
|
||||
test "decode 1970-01-01T23:59:60Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, :undefined, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60.5Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 500000} == RFC3339.parse("23:59:60.5Z")
|
||||
test "decode 1970-01-01T23:59:60.5Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 500000, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60.5Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60.55Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 550000} == RFC3339.parse("23:59:60.55Z")
|
||||
test "decode 1970-01-01T23:59:60.55Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 550000, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60.55Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60.555555Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 555555} == RFC3339.parse("23:59:60.555555Z")
|
||||
test "decode 1970-01-01T23:59:60.555555Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 555555, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60.555555Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60.5555554Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 555555} == RFC3339.parse("23:59:60.5555554Z")
|
||||
test "decode 1970-01-01T23:59:60.5555554Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 555555, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60.5555554Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60.999999Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 999999} == RFC3339.parse("23:59:60.999999Z")
|
||||
test "decode 1970-01-01T23:59:60.999999Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 999999, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60.999999Z")
|
||||
end
|
||||
|
||||
test "decode 23:59:60.9999999Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 999999} == RFC3339.parse("23:59:60.9999999Z")
|
||||
test "decode 1970-01-01T23:59:60.9999999Z" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 999999, :undefined}} = :rfc3339.parse("1970-01-01T23:59:60.9999999Z")
|
||||
end
|
||||
|
||||
test "decode 00:00:00+00:00" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0, tz_offset: 0} == RFC3339.parse("00:00:00+00:00")
|
||||
test "decode 1970-01-01T00:00:00+00:00" do
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, :undefined, 0}} = :rfc3339.parse("1970-01-01T00:00:00+00:00")
|
||||
end
|
||||
|
||||
test "decode 00:00:00-00:00" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0, tz_offset: 0} == RFC3339.parse("00:00:00-00:00")
|
||||
test "decode 1970-01-01T00:00:00-00:00" do
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, :undefined, 0}} = :rfc3339.parse("1970-01-01T00:00:00-00:00")
|
||||
end
|
||||
|
||||
test "decode 00:00:00+23:59" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0, tz_offset: 1439} == RFC3339.parse("00:00:00+23:59")
|
||||
test "decode 1970-01-01T00:00:00+23:59" do
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, :undefined, 1439}} = :rfc3339.parse("1970-01-01T00:00:00+23:59")
|
||||
end
|
||||
|
||||
test "decode 23:59:60+00:00" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, tz_offset: 0} == RFC3339.parse("23:59:60+00:00")
|
||||
test "decode 1970-01-01T23:59:60+00:00" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, :undefined, 0}} = :rfc3339.parse("1970-01-01T23:59:60+00:00")
|
||||
end
|
||||
|
||||
test "decode 23:59:60+23:59" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, tz_offset: 1439} == RFC3339.parse("23:59:60+23:59")
|
||||
test "decode 1970-01-01T23:59:60+23:59" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, :undefined, 1439}} = :rfc3339.parse("1970-01-01T23:59:60+23:59")
|
||||
end
|
||||
|
||||
test "decode 23:59:60-23:59" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, tz_offset: -1439} == RFC3339.parse("23:59:60-23:59")
|
||||
test "decode 1970-01-01T23:59:60-23:59" do
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, :undefined, -1439}} = :rfc3339.parse("1970-01-01T23:59:60-23:59")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03Z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3} == RFC3339.parse("1979-06-21T22:20:03Z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, :undefined}} = :rfc3339.parse("1979-06-21T22:20:03Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21t22:20:03z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3} == RFC3339.parse("1979-06-21t22:20:03z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, :undefined}} = :rfc3339.parse("1979-06-21t22:20:03z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21 22:20:03Z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3} == RFC3339.parse("1979-06-21 22:20:03Z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, :undefined}} = :rfc3339.parse("1979-06-21 22:20:03Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03.9876543Z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3, usec: 987654} == RFC3339.parse("1979-06-21T22:20:03.9876543Z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 987654, :undefined}} = :rfc3339.parse("1979-06-21T22:20:03.9876543Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03+02:00" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3, tz_offset: 120} == RFC3339.parse("1979-06-21T22:20:03+02:00")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, 120}} = :rfc3339.parse("1979-06-21T22:20:03+02:00")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03.9876543+02:00" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3, usec: 987654, tz_offset: 120} == RFC3339.parse("1979-06-21T22:20:03.9876543+02:00")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 987654, 120}} = :rfc3339.parse("1979-06-21T22:20:03.9876543+02:00")
|
||||
end
|
||||
|
||||
test "encode 1979-06-21" do
|
||||
assert "1979-06-21" == RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21})
|
||||
end
|
||||
|
||||
test "encode 12:12:12Z" do
|
||||
assert "12:12:12Z" == RFC3339.format(%RFC3339{hour: 12, min: 12, sec: 12})
|
||||
end
|
||||
|
||||
test "encode 12:12:12.120000Z" do
|
||||
assert "12:12:12.120000Z" == RFC3339.format(%RFC3339{hour: 12, min: 12, sec: 12, usec: 120000})
|
||||
end
|
||||
|
||||
test "encode 12:12:12.000012Z" do
|
||||
assert "12:12:12.000012Z" == RFC3339.format(%RFC3339{hour: 12, min: 12, sec: 12, usec: 12})
|
||||
end
|
||||
|
||||
test "encode 12:12:12+12:12" do
|
||||
assert "12:12:12+12:12" == RFC3339.format(%RFC3339{hour: 12, min: 12, sec: 12, tz_offset: 732})
|
||||
end
|
||||
|
||||
test "encode 12:12:12-12:12" do
|
||||
assert "12:12:12-12:12" == RFC3339.format(%RFC3339{hour: 12, min: 12, sec: 12, tz_offset: -732})
|
||||
assert "1979-06-21T00:00:00Z" = :rfc3339.format({{1979, 6, 21}, {0, 0, 0}, :undefined, :undefined})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12Z" do
|
||||
assert "1979-06-21T12:12:12Z" == RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21, hour: 12, min: 12, sec: 12})
|
||||
assert "1979-06-21T12:12:12Z" = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, :undefined, :undefined})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.120000Z" do
|
||||
assert "1979-06-21T12:12:12.120000Z" = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 120000, :undefined})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.000012Z" do
|
||||
assert "1979-06-21T12:12:12.000012Z" = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 12, :undefined})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12+12:12" do
|
||||
assert "1979-06-21T12:12:12+12:12" = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, :undefined, 732})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12-12:12" do
|
||||
assert "1979-06-21T12:12:12-12:12" = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, :undefined, -732})
|
||||
end
|
||||
end
|
||||
|
||||
defmodule RFC3339Test do
|
||||
use ExUnit.Case
|
||||
|
||||
test "decode 0000-01-01T00:00:00.0+00:00" do
|
||||
assert %RFC3339{year: 0, month: 1, day: 1} = RFC3339.parse("0000-01-01T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 9999-12-31T00:00:00.0+00:00" do
|
||||
assert %RFC3339{year: 9999, month: 12, day: 31} = RFC3339.parse("9999-12-31T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 1584-03-04T00:00:00.0+00:00" do
|
||||
assert %RFC3339{year: 1584, month: 3, day: 4} = RFC3339.parse("1584-03-04T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 1900-01-01T00:00:00.0+00:00" do
|
||||
assert %RFC3339{year: 1900, month: 1, day: 1} = RFC3339.parse("1900-01-01T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 2016-01-24T00:00:00.0+00:00" do
|
||||
assert %RFC3339{year: 2016, month: 1, day: 24} = RFC3339.parse("2016-01-24T00:00:00.0+00:00")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T00:00:00Z" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0} = RFC3339.parse("1970-01-01T00:00:00Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60} = RFC3339.parse("1970-01-01T23:59:60Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60.5Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 500000} = RFC3339.parse("1970-01-01T23:59:60.5Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60.55Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 550000} = RFC3339.parse("1970-01-01T23:59:60.55Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60.555555Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 555555} = RFC3339.parse("1970-01-01T23:59:60.555555Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60.5555554Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 555555} = RFC3339.parse("1970-01-01T23:59:60.5555554Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60.999999Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 999999} = RFC3339.parse("1970-01-01T23:59:60.999999Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60.9999999Z" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, usec: 999999} = RFC3339.parse("1970-01-01T23:59:60.9999999Z")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T00:00:00+00:00" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0, tz_offset: 0} = RFC3339.parse("1970-01-01T00:00:00+00:00")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T00:00:00-00:00" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0, tz_offset: 0} = RFC3339.parse("1970-01-01T00:00:00-00:00")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T00:00:00+23:59" do
|
||||
assert %RFC3339{hour: 0, min: 0, sec: 0, tz_offset: 1439} = RFC3339.parse("1970-01-01T00:00:00+23:59")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60+00:00" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, tz_offset: 0} = RFC3339.parse("1970-01-01T23:59:60+00:00")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60+23:59" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, tz_offset: 1439} = RFC3339.parse("1970-01-01T23:59:60+23:59")
|
||||
end
|
||||
|
||||
test "decode 1970-01-01T23:59:60-23:59" do
|
||||
assert %RFC3339{hour: 23, min: 59, sec: 60, tz_offset: -1439} = RFC3339.parse("1970-01-01T23:59:60-23:59")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03Z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3} = RFC3339.parse("1979-06-21T22:20:03Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21t22:20:03z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3} = RFC3339.parse("1979-06-21t22:20:03z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21 22:20:03Z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3} = RFC3339.parse("1979-06-21 22:20:03Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03.9876543Z" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3, usec: 987654} = RFC3339.parse("1979-06-21T22:20:03.9876543Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03+02:00" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3, tz_offset: 120} = RFC3339.parse("1979-06-21T22:20:03+02:00")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03.9876543+02:00" do
|
||||
assert %RFC3339{year: 1979, month: 6, day: 21, hour: 22, min: 20, sec: 3, usec: 987654, tz_offset: 120} = RFC3339.parse("1979-06-21T22:20:03.9876543+02:00")
|
||||
end
|
||||
|
||||
test "encode 1979-06-21" do
|
||||
assert "1979-06-21T00:00:00Z" = RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12Z" do
|
||||
assert "1979-06-21T12:12:12Z" = RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21, hour: 12, min: 12, sec: 12})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.120000Z" do
|
||||
assert "1979-06-21T12:12:12.120000Z" = RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21, hour: 12, min: 12, sec: 12, usec: 120000})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.000012Z" do
|
||||
assert "1979-06-21T12:12:12.000012Z" = RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21,hour: 12, min: 12, sec: 12, usec: 12})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12+12:12" do
|
||||
assert "1979-06-21T12:12:12+12:12" = RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21, hour: 12, min: 12, sec: 12, tz_offset: 732})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12-12:12" do
|
||||
assert "1979-06-21T12:12:12-12:12" = RFC3339.format(%RFC3339{year: 1979, month: 6, day: 21, hour: 12, min: 12, sec: 12, tz_offset: -732})
|
||||
end
|
||||
end
|
||||
|
||||
defmodule :rfc3339_maps do
|
||||
use ExUnit.Case
|
||||
|
||||
test "to_map 1979-06-21T12:12:12.12+12:12" do
|
||||
assert %{:year => 1979, :month => 6, :day => 21,
|
||||
:hour => 12, :min => 12, :sec => 12,
|
||||
:usec => 120000, :tz_offset => 732} = :rfc3339.to_map("1979-06-21T12:12:12.12+12:12")
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.12+12:12" do
|
||||
assert "1979-06-21T12:12:12.120000+12:12" = :rfc3339.format(%{:year => 1979, :month => 6, :day => 21,
|
||||
:hour => 12, :min => 12, :sec => 12,
|
||||
:usec => 120000, :tz_offset => 732})
|
||||
end
|
||||
|
||||
test "to_map 1979-06-21T12:12:12Z" do
|
||||
assert %{:year => 1979, :month => 6, :day => 21,
|
||||
:hour => 12, :min => 12, :sec => 12} = :rfc3339.to_map("1979-06-21T12:12:12Z")
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12Z" do
|
||||
assert "1979-06-21T12:12:12Z" = :rfc3339.format(%{:year => 1979, :month => 6, :day => 21,
|
||||
:hour => 12, :min => 12, :sec => 12})
|
||||
end
|
||||
end
|
||||
|
||||
defmodule RFC3339Test.Errors do
|
||||
use ExUnit.Case
|
||||
|
||||
test "badarg" do
|
||||
assert {:error, :badarg} = RFC3339.parse(1)
|
||||
end
|
||||
|
||||
test "baddate" do
|
||||
assert {:error, :baddate} = RFC3339.parse("79-06-21T00:00:00Z")
|
||||
end
|
||||
|
||||
test "badyear" do
|
||||
assert {:error, :badyear} = RFC3339.parse("xxxx-06-21T00:00:00Z")
|
||||
end
|
||||
|
||||
test "badmonth" do
|
||||
assert {:error, :badmonth} = RFC3339.parse("1979-xx-21T00:00:00Z")
|
||||
end
|
||||
|
||||
test "badday" do
|
||||
assert {:error, :badday} = RFC3339.parse("1979-06-xxT00:00:00Z")
|
||||
end
|
||||
|
||||
test "badtime" do
|
||||
assert {:error, :badtime} = RFC3339.parse("1979-06-21-00:00:00Z")
|
||||
end
|
||||
|
||||
test "badhour" do
|
||||
assert {:error, :badhour} = RFC3339.parse("1979-06-21Txx:00:00Z")
|
||||
end
|
||||
|
||||
test "badmin" do
|
||||
assert {:error, :badminute} = RFC3339.parse("1979-06-21T00:xx:00Z")
|
||||
end
|
||||
|
||||
test "badsec" do
|
||||
assert {:error, :badsecond} = RFC3339.parse("1979-06-21T00:00:xxZ")
|
||||
end
|
||||
|
||||
test "badtz" do
|
||||
assert {:error, :badtimezone} = RFC3339.parse("1979-06-21T00:00:00x")
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user