mirror of
https://github.com/valitydev/rfc3339.git
synced 2024-11-06 00:25:16 +00:00
Merge pull request #1 from rbkmoney/fix/follow-spec-strictly
Make library follow declared spec strictly
This commit is contained in:
commit
ee3a1a6b1e
2
mix.exs
2
mix.exs
@ -1,7 +1,7 @@
|
||||
defmodule RFC3339.Mixfile do
|
||||
use Mix.Project
|
||||
|
||||
@version "0.2.1"
|
||||
@version "0.2.2"
|
||||
|
||||
def project do
|
||||
[app: :rfc3339,
|
||||
|
@ -1,7 +1,7 @@
|
||||
{application, rfc3339,
|
||||
[
|
||||
{description, "an rfc3339 parser and formatter"},
|
||||
{vsn, "0.2.1"},
|
||||
{vsn, "0.2.2"},
|
||||
{modules, [rfc3339]},
|
||||
{registered, []},
|
||||
{applications, [
|
||||
|
@ -24,25 +24,34 @@
|
||||
-type tz() :: tz_offset().
|
||||
-type tz_offset() :: -1439..1439.
|
||||
|
||||
-type timestamp() :: {date(), time(), usec(), tz()}.
|
||||
|
||||
-type maybe(T) :: T | undefined | nil.
|
||||
-type timestamp_map() :: #{
|
||||
year => maybe(year()),
|
||||
month => maybe(month()),
|
||||
day => maybe(day()),
|
||||
hour => maybe(hour()),
|
||||
min => maybe(min()),
|
||||
sec => maybe(sec()),
|
||||
usec => maybe(usec()),
|
||||
tz_offset => maybe(tz_offset())
|
||||
}.
|
||||
|
||||
-type error() :: badarg | baddate | badtime | badyear | badday | badhour | badminute | badsecond | badusec | badtimezone.
|
||||
|
||||
%% -spec parse_to_local_datetime(binary()) -> {date(), time()}
|
||||
-spec parse_to_local_datetime(binary()) -> {date(), time()}.
|
||||
parse_to_local_datetime(Bin) ->
|
||||
{ok, {Date, Time, _, TZ}} = parse(Bin),
|
||||
TZSecs = calendar:datetime_to_gregorian_seconds({Date, Time}),
|
||||
UTCDateTime = calendar:gregorian_seconds_to_datetime(case TZ of
|
||||
_ when is_integer(TZ) ->
|
||||
TZSecs + (60*TZ);
|
||||
_ ->
|
||||
TZSecs
|
||||
end),
|
||||
UTCDateTime = calendar:gregorian_seconds_to_datetime(TZSecs + (60*TZ)),
|
||||
calendar:universal_time_to_local_time(UTCDateTime).
|
||||
|
||||
-spec parse(binary()) -> {ok, {date(), time(), usec(), tz()}} | {error, error()}.
|
||||
-spec parse(binary()) -> {ok, timestamp()} | {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()}.
|
||||
-spec to_map(binary()) -> {ok, timestamp_map()} | {error, error()}.
|
||||
to_map(Bin) when is_binary(Bin) ->
|
||||
case parse(Bin) of
|
||||
{ok, {Date, Time, USec, Tz}} -> mapify(Date, Time, USec, Tz, #{});
|
||||
@ -58,13 +67,13 @@ to_time(Bin, Unit) when is_binary(Bin) ->
|
||||
case parse(Bin) of
|
||||
{ok, {Date, {Hour, Min, Sec}, USec, Tz}} ->
|
||||
Epoch = calendar:datetime_to_gregorian_seconds({{1970, 1, 1}, {0, 0, 0}}),
|
||||
Time = {Hour, Min + or_zero(Tz), Sec},
|
||||
Time = {Hour, Min + Tz, Sec},
|
||||
GregorianSeconds = calendar:datetime_to_gregorian_seconds({Date, Time}),
|
||||
U = erlang:convert_time_unit(or_zero(USec), micro_seconds, Unit),
|
||||
U = erlang:convert_time_unit(USec, micro_seconds, Unit),
|
||||
{ok, erlang:convert_time_unit(GregorianSeconds - Epoch, seconds, Unit) + U};
|
||||
{error, Error} -> {error, Error}
|
||||
end.
|
||||
|
||||
|
||||
|
||||
mapify({Year, Month, Day}, Time, USec, Tz, Result)
|
||||
when is_integer(Year), is_integer(Month), is_integer(Day) ->
|
||||
@ -76,12 +85,10 @@ 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(Tz, Result) when is_integer(Tz) -> maps:merge(Result, #{tz_offset => Tz});
|
||||
mapify(_, _) -> {error, badarg}.
|
||||
|
||||
@ -90,16 +97,15 @@ mapify(Time) when is_integer(Time) ->
|
||||
GregorianSeconds = Time div 1000000 + Epoch,
|
||||
{{Year, Month, Day}, {Hour, Min, Sec}} = calendar:gregorian_seconds_to_datetime(GregorianSeconds),
|
||||
USec = Time rem 1000000,
|
||||
#{year => Year, month => Month, day => Day, hour => Hour, min => Min, sec => Sec, usec => USec};
|
||||
mapify(_) -> {error, badarg}.
|
||||
#{year => Year, month => Month, day => Day, hour => Hour, min => Min, sec => Sec, usec => USec}.
|
||||
|
||||
-spec format(map() | {date(), time(), usec(), tz()} | datetime() | integer()) -> {ok, binary()} | {error, error()}.
|
||||
-spec format(timestamp_map() | timestamp() | datetime() | integer()) -> {ok, binary()} | {error, error()}.
|
||||
format({Date, Time, USec, Tz})
|
||||
when is_tuple(Date), is_tuple(Time) ->
|
||||
format(mapify(Date, Time, USec, Tz, #{}));
|
||||
format({Date, Time})
|
||||
when is_tuple(Date), is_tuple(Time) ->
|
||||
format(mapify(Date, Time, undefined, undefined, #{}));
|
||||
format(mapify(Date, Time, 0, 0, #{}));
|
||||
format(Time) when is_integer(Time) ->
|
||||
%% USec is the greatest fidelity supported. nano seconds are converted lossily
|
||||
USec = erlang:convert_time_unit(Time, native, micro_seconds),
|
||||
@ -138,7 +144,8 @@ 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).
|
||||
usec_or_tz(Rest, {Date, Time, undefined, undefined}) ->
|
||||
tz(Rest, {Date, Time, 0, undefined}).
|
||||
|
||||
%% next two clauses burn off fractional seconds beyond microsecond precision
|
||||
usec(<<X, Rest/binary>>, Result, USec, 0)
|
||||
@ -154,8 +161,7 @@ when X >= $0 andalso X =< $9 ->
|
||||
end;
|
||||
%% not a digit, insert usecs into time and proceed to tz
|
||||
usec(Bin, {Date, Time, undefined, undefined}, USec, _) ->
|
||||
tz(Bin, {Date, Time, USec, undefined});
|
||||
usec(_, _, _, _) -> {error, badusec}.
|
||||
tz(Bin, {Date, Time, USec, undefined}).
|
||||
|
||||
tz(<<$+, H1, H2, $:, M1, M2>>, {Date, Time, USec, undefined}) ->
|
||||
Hour = to_hour(H1, H2),
|
||||
@ -171,10 +177,10 @@ tz(<<$-, H1, H2, $:, M1, M2>>, {Date, Time, USec, undefined}) ->
|
||||
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(<<$Z>>, {Date, Time, USec, undefined}) ->
|
||||
{ok, {Date, Time, USec, 0}};
|
||||
tz(<<$z>>, {Date, Time, USec, undefined}) ->
|
||||
{ok, {Date, Time, USec, 0}};
|
||||
tz(_, _) -> {error, badtimezone}.
|
||||
|
||||
calc_tz(_, {error, Error}, _) -> {error, Error};
|
||||
@ -278,8 +284,6 @@ format_time(H, M, S, U) when is_integer(H), is_integer(M), is_integer(S), is_int
|
||||
io_lib:format("~2.10.0B:~2.10.0B:~9.6.0f", [H, M, SU]);
|
||||
format_time(_, _, _, _) -> {error, badtime}.
|
||||
|
||||
format_offset(undefined) -> "Z";
|
||||
format_offset(nil) -> "Z";
|
||||
format_offset(0) -> "Z";
|
||||
format_offset(M) when is_integer(M) ->
|
||||
Sign = case M >= 0 of true -> "+"; false -> "-" end,
|
||||
@ -300,6 +304,3 @@ g(Key, Map) ->
|
||||
undefined -> 0;
|
||||
Val -> Val
|
||||
end.
|
||||
|
||||
or_zero(undefined) -> 0;
|
||||
or_zero(N) when is_integer(N) -> N.
|
||||
|
@ -22,79 +22,79 @@ defmodule :rfc3339_test do
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("1970-01-01T00:00:00Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 0, 0}} = :rfc3339.parse("1970-01-01T23:59:60Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 500000, 0}} = :rfc3339.parse("1970-01-01T23:59:60.5Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 550000, 0}} = :rfc3339.parse("1970-01-01T23:59:60.55Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 555555, 0}} = :rfc3339.parse("1970-01-01T23:59:60.555555Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 555555, 0}} = :rfc3339.parse("1970-01-01T23:59:60.5555554Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 999999, 0}} = :rfc3339.parse("1970-01-01T23:59:60.999999Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 999999, 0}} = :rfc3339.parse("1970-01-01T23:59:60.9999999Z")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("1970-01-01T00:00:00+00:00")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, 0, 0}} = :rfc3339.parse("1970-01-01T00:00:00-00:00")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {0, 0, 0}, 0, 1439}} = :rfc3339.parse("1970-01-01T00:00:00+23:59")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 0, 0}} = :rfc3339.parse("1970-01-01T23:59:60+00:00")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 0, 1439}} = :rfc3339.parse("1970-01-01T23:59:60+23:59")
|
||||
end
|
||||
|
||||
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")
|
||||
assert {:ok, {{1970, 1, 1}, {23, 59, 60}, 0, -1439}} = :rfc3339.parse("1970-01-01T23:59:60-23:59")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03Z" do
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, :undefined}} = :rfc3339.parse("1979-06-21T22:20:03Z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 0, 0}} = :rfc3339.parse("1979-06-21T22:20:03Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21t22:20:03z" do
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, :undefined}} = :rfc3339.parse("1979-06-21t22:20:03z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 0, 0}} = :rfc3339.parse("1979-06-21t22:20:03z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21 22:20:03Z" do
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, :undefined}} = :rfc3339.parse("1979-06-21 22:20:03Z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 0, 0}} = :rfc3339.parse("1979-06-21 22:20:03Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03.9876543Z" do
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 987654, :undefined}} = :rfc3339.parse("1979-06-21T22:20:03.9876543Z")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 987654, 0}} = :rfc3339.parse("1979-06-21T22:20:03.9876543Z")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03+02:00" do
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, :undefined, 120}} = :rfc3339.parse("1979-06-21T22:20:03+02:00")
|
||||
assert {:ok, {{1979, 6, 21}, {22, 20, 03}, 0, 120}} = :rfc3339.parse("1979-06-21T22:20:03+02:00")
|
||||
end
|
||||
|
||||
test "decode 1979-06-21T22:20:03.9876543+02:00" do
|
||||
@ -102,27 +102,27 @@ defmodule :rfc3339_test do
|
||||
end
|
||||
|
||||
test "encode 1979-06-21" do
|
||||
assert {:ok, "1979-06-21T00:00:00Z"} = :rfc3339.format({{1979, 6, 21}, {0, 0, 0}, :undefined, :undefined})
|
||||
assert {:ok, "1979-06-21T00:00:00Z"} = :rfc3339.format({{1979, 6, 21}, {0, 0, 0}, 0, 0})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12Z" do
|
||||
assert {:ok, "1979-06-21T12:12:12Z"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, :undefined, :undefined})
|
||||
assert {:ok, "1979-06-21T12:12:12Z"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 0, 0})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.120000Z" do
|
||||
assert {:ok, "1979-06-21T12:12:12.120000Z"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 120000, :undefined})
|
||||
assert {:ok, "1979-06-21T12:12:12.120000Z"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 120000, 0})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12.000012Z" do
|
||||
assert {:ok, "1979-06-21T12:12:12.000012Z"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 12, :undefined})
|
||||
assert {:ok, "1979-06-21T12:12:12.000012Z"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 12, 0})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12+12:12" do
|
||||
assert {:ok, "1979-06-21T12:12:12+12:12"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, :undefined, 732})
|
||||
assert {:ok, "1979-06-21T12:12:12+12:12"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 0, 732})
|
||||
end
|
||||
|
||||
test "encode 1979-06-21T12:12:12-12:12" do
|
||||
assert {:ok, "1979-06-21T12:12:12-12:12"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, :undefined, -732})
|
||||
assert {:ok, "1979-06-21T12:12:12-12:12"} = :rfc3339.format({{1979, 6, 21}, {12, 12, 12}, 0, -732})
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user