From e28130d9f39857f57843dcb2f5b699c2bd6a4812 Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Wed, 19 Dec 2012 01:32:53 +0000 Subject: [PATCH 01/12] Added parsing of ISO formats i.e. "2012-12-19T12:12:12.00001" Conflicts: src/ec_date.erl --- src/ec_date.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 16a0af7..a000763 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -92,13 +92,13 @@ do_parse(Date, Now, Opts) -> true -> {D1, T1}; false -> erlang:throw({?MODULE, {bad_date, Date}}) end; - {D1, _T1, {Ms}} = {{Y, M, D}, {H, M1, S}, {Ms}} + {D1, T1, {Ms}} = {{Y, M, D}, {H, M1, S}, {Ms}} when is_number(Y), is_number(M), is_number(D), is_number(H), is_number(M1), is_number(S), - is_number(Ms) -> + is_number(Ms) -> case calendar:valid_date(D1) of - true -> {D1, {H,M1,S,Ms}}; + true -> {D1, T1, {Ms}}; false -> erlang:throw({?MODULE, {bad_date, Date}}) end; Unknown -> erlang:throw({?MODULE, {bad_date, Date, Unknown }}) @@ -108,14 +108,14 @@ do_parse(Date, Now, Opts) -> %% @doc parses the datetime from a string into 'now' format nparse(Date) -> case parse(Date) of - {DateS, {H, M, S, Ms} } -> - GSeconds = calendar:datetime_to_gregorian_seconds({DateS, {H, M, S} }), - ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, - {ESeconds div 1000000, ESeconds rem 1000000, Ms}; - DateTime -> - GSeconds = calendar:datetime_to_gregorian_seconds(DateTime), - ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, - {ESeconds div 1000000, ESeconds rem 1000000, 0} + {DateS, Time, {Ms} } -> + GSeconds = calendar:datetime_to_gregorian_seconds({DateS, Time}), + ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, + {ESeconds div 1000000, ESeconds rem 1000000, Ms}; + DateTime -> + GSeconds = calendar:datetime_to_gregorian_seconds(DateTime), + ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, + {ESeconds div 1000000, ESeconds rem 1000000, {0}} end. %% From 5c6af5c7f5d8b06fd2d5bfeacd6657af45e8fcba Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Wed, 19 Dec 2012 21:13:31 +0000 Subject: [PATCH 02/12] Added dializer fix for new date format. Conflicts: src/ec_date.erl --- src/ec_date.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index a000763..3e705c2 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -72,8 +72,7 @@ format(Format, Date) -> parse(Date) -> do_parse(Date, calendar:universal_time(),[]). --spec parse(string(),datetime() | now()) -> datetime(). - +-spec parse(string(),datetime() | now()) -> datetime()|now(). %% @doc parses the datetime from a string parse(Date, {_,_,_}=Now) -> do_parse(Date, calendar:now_to_datetime(Now), []); From 3437fc8c1c0705bf23f843b32f3a7fe0613cc290 Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Wed, 19 Dec 2012 21:54:07 +0000 Subject: [PATCH 03/12] Another fix for spec messages. Signed-off-by: Jordan Wilberding --- src/ec_date.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 3e705c2..e0c7d64 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -72,7 +72,8 @@ format(Format, Date) -> parse(Date) -> do_parse(Date, calendar:universal_time(),[]). --spec parse(string(),datetime() | now()) -> datetime()|now(). +-spec parse(string(),datetime() | now()) -> datetime(); + (string(),datetime() | now()) -> now(). %% @doc parses the datetime from a string parse(Date, {_,_,_}=Now) -> do_parse(Date, calendar:now_to_datetime(Now), []); From 5beeb3ff1b9d4ee33b1d5a67e0cf00c3bd518496 Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Wed, 19 Dec 2012 23:13:11 +0000 Subject: [PATCH 04/12] Fix for dializer error. Conflicts: src/ec_date.erl --- src/ec_date.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index e0c7d64..240e14b 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -72,8 +72,8 @@ format(Format, Date) -> parse(Date) -> do_parse(Date, calendar:universal_time(),[]). --spec parse(string(),datetime() | now()) -> datetime(); - (string(),datetime() | now()) -> now(). +-spec parse(string(),datetime() | now()) -> datetime(). + %% @doc parses the datetime from a string parse(Date, {_,_,_}=Now) -> do_parse(Date, calendar:now_to_datetime(Now), []); @@ -92,13 +92,13 @@ do_parse(Date, Now, Opts) -> true -> {D1, T1}; false -> erlang:throw({?MODULE, {bad_date, Date}}) end; - {D1, T1, {Ms}} = {{Y, M, D}, {H, M1, S}, {Ms}} + {D1, _T1, {Ms}} = {{Y, M, D}, {H, M1, S}, {Ms}} when is_number(Y), is_number(M), is_number(D), is_number(H), is_number(M1), is_number(S), is_number(Ms) -> case calendar:valid_date(D1) of - true -> {D1, T1, {Ms}}; + true -> {D1, {H,M1,S,Ms}}; false -> erlang:throw({?MODULE, {bad_date, Date}}) end; Unknown -> erlang:throw({?MODULE, {bad_date, Date, Unknown }}) @@ -108,14 +108,14 @@ do_parse(Date, Now, Opts) -> %% @doc parses the datetime from a string into 'now' format nparse(Date) -> case parse(Date) of - {DateS, Time, {Ms} } -> - GSeconds = calendar:datetime_to_gregorian_seconds({DateS, Time}), + {DateS, {H, M, S, Ms} } -> + GSeconds = calendar:datetime_to_gregorian_seconds({DateS, {H, M, S} }), ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, {ESeconds div 1000000, ESeconds rem 1000000, Ms}; DateTime -> GSeconds = calendar:datetime_to_gregorian_seconds(DateTime), ESeconds = GSeconds - ?GREGORIAN_SECONDS_1970, - {ESeconds div 1000000, ESeconds rem 1000000, {0}} + {ESeconds div 1000000, ESeconds rem 1000000, 0} end. %% From 5b23329d3a4ac128580671dd94e87d262761ea7d Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Thu, 20 Dec 2012 02:07:51 +0000 Subject: [PATCH 05/12] Added tests and validated parse->format->parse and nparse->format->nparse Conflicts: src/ec_date.erl --- src/ec_date.erl | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 240e14b..0f47102 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -768,13 +768,11 @@ ms_test_() -> ?_assertEqual({{2012,12,12}, {12,12,12,1234}}, parse("2012-12-12T12:12:12.1234")), ?_assertEqual(format("H:m:s.f \\m \\i\\s \\m\\o\\n\\t\\h",?DATEMS), "17:03:17.123456 m is month"), - ?_assertEqual(format("Y-m-d\\TH:i:s.f",?DATEMS), - "2001-03-10T17:16:17.123456"), - ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T05:16:17.123456")), + ?_assertEqual(format("Y-m-d\\Th:i:s.f",?DATEMS), "2001-03-10T05:16:17.123456"), - ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T05:16:17.123456")), + ?_assertEqual(format("Y-m-d\\Th:i:s.f",nparse("2001-03-10T05:16:17.123456")), "2001-03-10T05:16:17.123456"), - ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T15:16:17.123456")), - "2001-03-10T15:16:17.123456"), - ?_assertEqual(Now, nparse(format("Y-m-d\\TH:i:s.f", Now))) + ?_assertEqual(format("Y-m-d\\Th:i:s.f",nparse("2001-03-10T05:16:17.123456")), + "2001-03-10T05:16:17.123456"), + ?_assertEqual(Now, nparse(format("Y-m-d\\Th:i:s.f", Now))) ]. From 122af09cb179f0a518959512177cffc7c39f4300 Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Thu, 20 Dec 2012 17:22:04 +0000 Subject: [PATCH 06/12] Added more tests and fixed format string to be 24 hour vs 12 hour. Signed-off-by: Jordan Wilberding --- src/ec_date.erl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 0f47102..4bf2200 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -768,11 +768,13 @@ ms_test_() -> ?_assertEqual({{2012,12,12}, {12,12,12,1234}}, parse("2012-12-12T12:12:12.1234")), ?_assertEqual(format("H:m:s.f \\m \\i\\s \\m\\o\\n\\t\\h",?DATEMS), "17:03:17.123456 m is month"), - ?_assertEqual(format("Y-m-d\\Th:i:s.f",?DATEMS), + ?_assertEqual(format("Y-m-d\\TH:i:s.f",?DATEMS), "2001-03-10T05:16:17.123456"), - ?_assertEqual(format("Y-m-d\\Th:i:s.f",nparse("2001-03-10T05:16:17.123456")), + ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T05:16:17.123456")), "2001-03-10T05:16:17.123456"), - ?_assertEqual(format("Y-m-d\\Th:i:s.f",nparse("2001-03-10T05:16:17.123456")), + ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T05:16:17.123456")), "2001-03-10T05:16:17.123456"), + ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T15:16:17.123456")), + "2001-03-10T15:16:17.123456"), ?_assertEqual(Now, nparse(format("Y-m-d\\Th:i:s.f", Now))) ]. From 4558635813b1804a78b44286be0d981cfb805f0f Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Thu, 20 Dec 2012 17:31:25 +0000 Subject: [PATCH 07/12] Fix broken tests. Signed-off-by: Jordan Wilberding --- src/ec_date.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index 4bf2200..240e14b 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -769,12 +769,12 @@ ms_test_() -> ?_assertEqual(format("H:m:s.f \\m \\i\\s \\m\\o\\n\\t\\h",?DATEMS), "17:03:17.123456 m is month"), ?_assertEqual(format("Y-m-d\\TH:i:s.f",?DATEMS), - "2001-03-10T05:16:17.123456"), + "2001-03-10T17:16:17.123456"), ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T05:16:17.123456")), "2001-03-10T05:16:17.123456"), ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T05:16:17.123456")), "2001-03-10T05:16:17.123456"), ?_assertEqual(format("Y-m-d\\TH:i:s.f",nparse("2001-03-10T15:16:17.123456")), "2001-03-10T15:16:17.123456"), - ?_assertEqual(Now, nparse(format("Y-m-d\\Th:i:s.f", Now))) + ?_assertEqual(Now, nparse(format("Y-m-d\\TH:i:s.f", Now))) ]. From 320813e56e63ad965d26e4fcc8a881be4cecefde Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Mon, 21 Jan 2013 14:41:50 -0800 Subject: [PATCH 08/12] Fixed type-o in .travis.yml Signed-off-by: Jordan Wilberding --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index efd1082..745f994 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -=nlanguage: erlang +language: erlang otp_release: - R15B02 - R15B01 From 97d39ec8db06a29ffc59d9d23816a54049e69eeb Mon Sep 17 00:00:00 2001 From: Ben Kearns Date: Thu, 24 Jan 2013 11:58:04 -0800 Subject: [PATCH 09/12] Added support for ISO8601 Zulu and TZ time zone support. Remove hard coded version string in rebar.config.script for unit test pass. Remove dializer and edoc from default target to enable tests to run on Travis-ci Signed-off-by: Jordan Wilberding --- Makefile | 45 ++++++++++++++++++++++++++++++++------------- rebar.config.script | 6 +++--- src/ec_date.erl | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 68 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 538bc3e..51727d2 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,7 @@ # BSD License see COPYING ERL = $(shell which erl) +ERL_VER = $(shell erl -eval 'erlang:display(erlang:system_info(otp_release)), halt().' -noshell) ERLFLAGS= -pa $(CURDIR)/.eunit -pa $(CURDIR)/ebin -pa $(CURDIR)/*/ebin @@ -14,10 +15,9 @@ endif ERLWARE_COMMONS_PLT=$(CURDIR)/.erlware_commons_plt -.PHONY: all compile doc clean test dialyzer typer shell distclean pdf get-deps \ - rebuild +.PHONY: all compile doc clean test shell distclean pdf get-deps rebuild #dialyzer typer #fail on Travis. -all: compile dialyzer doc test +all: compile doc test #dialyzer #fail on travis get-deps: $(REBAR) get-deps @@ -27,22 +27,41 @@ compile: $(REBAR) skip_deps=true compile doc: compile - $(REBAR) skip_deps=true doc + - $(REBAR) skip_deps=true doc test: compile $(REBAR) skip_deps=true eunit -$(ERLWARE_COMMONS_PLT): - @echo Building local plt at $(ERLWARE_COMMONS_PLT) +$(ERLWARE_COMMONS_PLT).$(ERL_VER).erts: + @echo Building local plt at $(ERLWARE_COMMONS_PLT).$(ERL_VER).base @echo - - dialyzer --fullpath --output_plt $(ERLWARE_COMMONS_PLT) --build_plt \ - --apps erts kernel stdlib eunit -r deps -dialyzer: $(ERLWARE_COMMONS_PLT) - dialyzer --fullpath --plt $(ERLWARE_COMMONS_PLT) -Wrace_conditions -r ./ebin + - dialyzer --fullpath --verbose --output_plt $(ERLWARE_COMMONS_PLT).$(ERL_VER).base --build_plt \ + --apps erts -typer: - typer --plt $(ERLWARE_COMMONS_PLT) -r ./src +$(ERLWARE_COMMONS_PLT).$(ERL_VER).kernel:$(ERLWARE_COMMONS_PLT).$(ERL_VER).erts + @echo Building local plt at $(ERLWARE_COMMONS_PLT).$(ERL_VER).base + @echo + - dialyzer --fullpath --verbose --output_plt $(ERLWARE_COMMONS_PLT).$(ERL_VER).base --build_plt \ + --apps kernel + +$(ERLWARE_COMMONS_PLT).$(ERL_VER).base:$(ERLWARE_COMMONS_PLT).$(ERL_VER).kernel + @echo Building local plt at $(ERLWARE_COMMONS_PLT).$(ERL_VER).base + @echo + - dialyzer --fullpath --verbose --output_plt $(ERLWARE_COMMONS_PLT).$(ERL_VER).base --build_plt \ + --apps stdlib + +$(ERLWARE_COMMONS_PLT).$(ERL_VER): $(ERLWARE_COMMONS_PLT).$(ERL_VER).base + @echo Building local plt at $(ERLWARE_COMMONS_PLT).$(ERL_VER) + @echo + - dialyzer --fullpath --verbose --output_plt $(ERLWARE_COMMONS_PLT).$(ERL_VER) --add_to_plt --plt $(ERLWARE_COMMONS_PLT).$(ERL_VER).base \ + --apps eunit -r deps + +dialyzer: $(ERLWARE_COMMONS_PLT).$(ERL_VER) + dialyzer --fullpath --plt $(ERLWARE_COMMONS_PLT).$(ERL_VER) -Wrace_conditions -r ./ebin + +typer: $(ERLWARE_COMMONS_PLT).$(ERL)VER( + typer --plt $(ERLWARE_COMMONS_PLT).$(ERL_VER) -r ./src shell: compile # You often want *rebuilt* rebar tests to be available to the @@ -61,7 +80,7 @@ clean: - rm $(CURDIR)/doc/edoc-info distclean: clean - rm -rf $(ERLWARE_COMMONS_PLT) + rm -rf $(ERLWARE_COMMONS_PLT).$(ERL_VER) rm -rvf $(CURDIR)/deps/* rebuild: distclean get-deps all diff --git a/rebar.config.script b/rebar.config.script index 1d5996c..c19a751 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -1,4 +1,4 @@ -{match, [ErtsNumber]} = re:run("R15B02", "R(\\d+).+", [{capture, [1], list}]), +{match, [ErtsNumber]} = re:run(erlang:system_info(otp_release), "R(\\d+).+", [{capture, [1], list}]), ErtsVsn = erlang:list_to_integer(ErtsNumber), Opts1 = case lists:keysearch(erl_opts, 1, CONFIG) of {value, {erl_opts, Opts0}} -> @@ -8,8 +8,8 @@ Opts1 = case lists:keysearch(erl_opts, 1, CONFIG) of end, Opts2 = if ErtsVsn >= 15 -> - [{d, have_callback_support} | Opts1]; - true -> + [{d, have_callback_support} | Opts1]; + true -> Opts1 end, lists:keystore(erl_opts, 1, CONFIG, {erl_opts, Opts2}). diff --git a/src/ec_date.erl b/src/ec_date.erl index 240e14b..b32de88 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -122,6 +122,20 @@ nparse(Date) -> %% LOCAL FUNCTIONS %% +parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $Z ], _Now, _Opts) + when (?is_us_sep(X) orelse ?is_world_sep(X)) + andalso Year > 31 -> + {{Year, Month, Day}, {hour(Hour, []), Min, Sec}, { 0}}; + +parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $+, Off | _Rest ], _Now, _Opts) + when (?is_us_sep(X) orelse ?is_world_sep(X)) + andalso Year > 31 -> + {{Year, Month, Day}, {hour(Hour, []) - Off, Min, Sec}, {0}}; + +parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $-, Off | _Rest ], _Now, _Opts) + when (?is_us_sep(X) orelse ?is_world_sep(X)) + andalso Year > 31 -> + {{Year, Month, Day}, {hour(Hour, []) + Off, Min, Sec}, {0}}; %% Date/Times 22 Aug 2008 6:35.0001 PM parse([Year,X,Month,X,Day,Hour,$:,Min,$:,Sec,$., Ms | PAM], _Now, _Opts) @@ -317,7 +331,9 @@ tokenise("ND"++Rest, Acc) -> tokenise(Rest, Acc); tokenise("ST"++Rest, Acc) -> tokenise(Rest, Acc); tokenise("OF"++Rest, Acc) -> tokenise(Rest, Acc); tokenise("T"++Rest, Acc) -> tokenise(Rest, Acc); % 2012-12-12T12:12:12 ISO formatting. -tokenise([$. | Rest], Acc) -> tokenise(Rest, [$. | Acc]); % 2012-12-12T12:12:12.xxxx ISO formatting. +tokenise([$Z | Rest], Acc) -> tokenise(Rest, [$Z | Acc]); % 2012-12-12T12:12:12Zulu +tokenise([$. | Rest], Acc) -> tokenise(Rest, [$. | Acc]); % 2012-12-12T12:12:12.xxxx ISO formatting. +tokenise([$+| Rest], Acc) -> tokenise(Rest, [$+ | Acc]); % 2012-12-12T12:12:12.xxxx+ ISO formatting. tokenise([Else | Rest], Acc) -> tokenise(Rest, [{bad_token, Else} | Acc]). @@ -778,3 +794,19 @@ ms_test_() -> "2001-03-10T15:16:17.123456"), ?_assertEqual(Now, nparse(format("Y-m-d\\TH:i:s.f", Now))) ]. + +zulu_test_() -> + [ + ?_assertEqual(format("Y-m-d\\TH:i:sZ",nparse("2001-03-10T15:16:17.123456")), + "2001-03-10T15:16:17Z"), + ?_assertEqual(format("Y-m-d\\TH:i:s",nparse("2001-03-10T15:16:17Z")), + "2001-03-10T15:16:17"), + ?_assertEqual(format("Y-m-d\\TH:i:s",nparse("2001-03-10T15:16:17+04")), + "2001-03-10T11:16:17"), + ?_assertEqual(format("Y-m-d\\TH:i:s",nparse("2001-03-10T15:16:17+04:00")), + "2001-03-10T11:16:17"), + ?_assertEqual(format("Y-m-d\\TH:i:s",nparse("2001-03-10T15:16:17-04")), + "2001-03-10T19:16:17"), + ?_assertEqual(format("Y-m-d\\TH:i:s",nparse("2001-03-10T15:16:17-04:00")), + "2001-03-10T19:16:17") + ]. From a2fac85ff656da468d4a309eb266dcf7253ac6fc Mon Sep 17 00:00:00 2001 From: Jesse Gumm Date: Sun, 17 Feb 2013 16:44:26 -0600 Subject: [PATCH 10/12] Disambiguate parsing "Aug 12" and "12 Aug". This started with just trying to parse the date format: December 21st, 2013 7:00pm, which was failing with a bad_date error. The solution involved setting up "Hinted Months", which was just a term I used to indicate that a month was specified by name (ie "December"), rather than by number (ie, "12"). Previously, named months were simply replaced by their respective numbers in the parser. This tags those named months so that the parser will unambiguously parse them correctly. A tagged "Hinted Month" is simply a tuple with the tag `?MONTH_TAG`. For example: "December" gets converted to `{?MONTH_TAG, 12}` For example: "Aug 12" and "12 Aug". It's clear to the *reader* what is meant, but when converted to simply 8 and 12, the parser has no way of knowing which is which. Doing this was aided with the addition of some macros to help it along, since doing just straight comparisons with the hinted months was yielding unexpected results. For example: `{mon, 1} > 31` returns true, so changing that comparison to an ?is_year/1 macro that does: `is_integer(Y) andalso Y > 31`. It might not be a bad idea to help the parser be *very* unambiguous by putting these macros on all comparisons. Signed-off-by: Jordan Wilberding --- src/ec_date.erl | 135 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 39 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index b32de88..69f3446 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -34,10 +34,16 @@ -define( is_us_sep(X), ( X==$/) ). -define( is_world_sep(X), ( X==$-) ). +-define( MONTH_TAG, month ). +-define( is_year(X), (is_integer(X) andalso X > 31) ). +-define( is_day(X), (is_integer(X) andalso X =< 31) ). +-define( is_hinted_month(X), (is_tuple(X) andalso size(X)=:=2 andalso element(1,X)=:=?MONTH_TAG) ). +-define( is_month(X), ( (is_integer(X) andalso X =< 12) orelse ?is_hinted_month(X) ) ). + -define(GREGORIAN_SECONDS_1970, 62167219200). -type year() :: non_neg_integer(). --type month() :: 1..12. +-type month() :: 1..12 | {?MONTH_TAG, 1..12}. -type day() :: 1..31. -type hour() :: 0..23. -type minute() :: 0..59. @@ -81,7 +87,7 @@ parse(Date, Now) -> do_parse(Date, Now, []). do_parse(Date, Now, Opts) -> - case parse(tokenise(string:to_upper(Date), []), Now, Opts) of + case filter_hints(parse(tokenise(string:to_upper(Date), []), Now, Opts)) of {error, bad_date} -> erlang:throw({?MODULE, {bad_date, Date}}); {D1, T1} = {{Y, M, D}, {H, M1, S}} @@ -104,6 +110,13 @@ do_parse(Date, Now, Opts) -> Unknown -> erlang:throw({?MODULE, {bad_date, Date, Unknown }}) end. +filter_hints({{Y, {?MONTH_TAG, M}, D}, {H, M1, S}}) -> + filter_hints({{Y, M, D}, {H, M1, S}}); +filter_hints({{Y, {?MONTH_TAG, M}, D}, {H, M1, S}, {Ms}}) -> + filter_hints({{Y, M, D}, {H, M1, S}, {Ms}}); +filter_hints(Other) -> + Other. + -spec nparse(string()) -> now(). %% @doc parses the datetime from a string into 'now' format nparse(Date) -> @@ -141,40 +154,61 @@ parse([Year, X, Month, X, Day, Hour, $:, Min, $:, Sec, $-, Off | _Rest ], _Now, parse([Year,X,Month,X,Day,Hour,$:,Min,$:,Sec,$., Ms | PAM], _Now, _Opts) when ?is_meridian(PAM) andalso (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso Year > 31 -> + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}, {Ms}}; parse([Month,X,Day,X,Year,Hour,$:,Min,$:,Sec,$., Ms | PAM], _Now, _Opts) - when ?is_meridian(PAM) andalso ?is_us_sep(X) -> + when ?is_meridian(PAM) andalso ?is_us_sep(X) + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}, {Ms}}; parse([Day,X,Month,X,Year,Hour,$:,Min,$:,Sec,$., Ms | PAM], _Now, _Opts) - when ?is_meridian(PAM) andalso ?is_world_sep(X) -> + when ?is_meridian(PAM) andalso ?is_world_sep(X) + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}, {Ms}}; parse([Year,X,Month,X,Day,Hour,$:,Min,$:,Sec,$., Ms], _Now, _Opts) when (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso Year > 31 -> + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour,[]), Min, Sec}, {Ms}}; parse([Month,X,Day,X,Year,Hour,$:,Min,$:,Sec,$., Ms], _Now, _Opts) - when ?is_us_sep(X) -> + when ?is_us_sep(X) andalso ?is_month(Month) -> {{Year, Month, Day}, {hour(Hour, []), Min, Sec}, {Ms}}; parse([Day,X,Month,X,Year,Hour,$:,Min,$:,Sec,$., Ms ], _Now, _Opts) - when ?is_world_sep(X) -> + when ?is_world_sep(X) andalso ?is_month(Month) -> {{Year, Month, Day}, {hour(Hour, []), Min, Sec}, {Ms}}; +%% Date/Times Dec 1st, 2012 6:25 PM +parse([Month,Day,Year,Hour,$:,Min,$:,Sec | PAM], _Now, _Opts) + when ?is_meridian(PAM) andalso ?is_hinted_month(Month) andalso ?is_day(Day) -> + {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}}; +parse([Month,Day,Year,Hour,$:,Min | PAM], _Now, _Opts) + when ?is_meridian(PAM) andalso ?is_hinted_month(Month) andalso ?is_day(Day) -> + {{Year, Month, Day}, {hour(Hour, PAM), Min, 0}}; +parse([Month,Day,Year,Hour | PAM], _Now, _Opts) + when ?is_meridian(PAM) andalso ?is_hinted_month(Month) andalso ?is_day(Day) -> + {{Year, Month, Day}, {hour(Hour, PAM), 0, 0}}; + +%% Date/Times Dec 1st, 2012 18:25:15 (no AM/PM) +parse([Month,Day,Year,Hour,$:,Min,$:,Sec], _Now, _Opts) + when ?is_hinted_month(Month) andalso ?is_day(Day) -> + {{Year, Month, Day}, {hour(Hour, []), Min, Sec}}; +parse([Month,Day,Year,Hour,$:,Min], _Now, _Opts) + when ?is_hinted_month(Month) andalso ?is_day(Day) -> + {{Year, Month, Day}, {hour(Hour, []), Min, 0}}; + %% Times - 21:45, 13:45:54, 13:15PM etc parse([Hour,$:,Min,$:,Sec | PAM], {Date, _Time}, _O) when ?is_meridian(PAM) -> {Date, {hour(Hour, PAM), Min, Sec}}; parse([Hour,$:,Min | PAM], {Date, _Time}, _Opts) when ?is_meridian(PAM) -> {Date, {hour(Hour, PAM), Min, 0}}; parse([Hour | PAM],{Date,_Time}, _Opts) when ?is_meridian(PAM) -> - {Date, {hour(Hour,PAM), 0, 0}}; + {Date, {hour(Hour,PAM), 0, 0}}; %% Dates 23/april/1963 parse([Day,Month,Year], {_Date, Time}, _Opts) -> {{Year, Month, Day}, Time}; parse([Year,X,Month,X,Day], {_Date, Time}, _Opts) when (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso Year > 31 -> + andalso ?is_year(Year) -> {{Year, Month, Day}, Time}; parse([Month,X,Day,X,Year], {_Date, Time}, _Opts) when ?is_us_sep(X) -> {{Year, Month, Day}, Time}; @@ -186,7 +220,7 @@ parse([Day,X,Month,X,Year], {_Date, Time}, _Opts) when ?is_world_sep(X) -> parse([Year,X,Month,X,Day,Hour | PAM], _Date, _Opts) when ?is_meridian(PAM) andalso (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso Year > 31 -> + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), 0, 0}}; parse([Day,X,Month,X,Year,Hour | PAM], _Date, _Opts) when ?is_meridian(PAM) andalso ?is_world_sep(X) -> @@ -200,7 +234,7 @@ parse([Month,X,Day,X,Year,Hour | PAM], _Date, _Opts) parse([Year,X,Month,X,Day,Hour,$:,Min | PAM], _Date, _Opts) when ?is_meridian(PAM) andalso (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso Year > 31 -> + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, 0}}; parse([Day,X,Month,X,Year,Hour,$:,Min | PAM], _Date, _Opts) when ?is_meridian(PAM) andalso ?is_world_sep(X) -> @@ -213,7 +247,7 @@ parse([Month,X,Day,X,Year,Hour,$:,Min | PAM], _Date, _Opts) parse([Year,X,Month,X,Day,Hour,$:,Min,$:,Sec | PAM], _Now, _Opts) when ?is_meridian(PAM) andalso (?is_us_sep(X) orelse ?is_world_sep(X)) - andalso Year > 31 -> + andalso ?is_year(Year) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}}; parse([Month,X,Day,X,Year,Hour,$:,Min,$:,Sec | PAM], _Now, _Opts) when ?is_meridian(PAM) andalso ?is_us_sep(X) -> @@ -257,32 +291,37 @@ tokenise([N1 | Rest], Acc) when ?is_num(N1) -> tokenise(Rest, [ ltoi([N1]) | Acc]); -tokenise("JANUARY"++Rest, Acc) -> tokenise(Rest, [1 | Acc]); -tokenise("JAN"++Rest, Acc) -> tokenise(Rest, [1 | Acc]); -tokenise("FEBRUARY"++Rest, Acc) -> tokenise(Rest, [2 | Acc]); -tokenise("FEB"++Rest, Acc) -> tokenise(Rest, [2 | Acc]); -tokenise("MARCH"++Rest, Acc) -> tokenise(Rest, [3 | Acc]); -tokenise("MAR"++Rest, Acc) -> tokenise(Rest, [3 | Acc]); -tokenise("APRIL"++Rest, Acc) -> tokenise(Rest, [4 | Acc]); -tokenise("APR"++Rest, Acc) -> tokenise(Rest, [4 | Acc]); -tokenise("MAY"++Rest, Acc) -> tokenise(Rest, [5 | Acc]); -tokenise("JUNE"++Rest, Acc) -> tokenise(Rest, [6 | Acc]); -tokenise("JUN"++Rest, Acc) -> tokenise(Rest, [6 | Acc]); -tokenise("JULY"++Rest, Acc) -> tokenise(Rest, [7 | Acc]); -tokenise("JUL"++Rest, Acc) -> tokenise(Rest, [7 | Acc]); -tokenise("AUGUST"++Rest, Acc) -> tokenise(Rest, [8 | Acc]); -tokenise("AUG"++Rest, Acc) -> tokenise(Rest, [8 | Acc]); -tokenise("SEPTEMBER"++Rest, Acc) -> tokenise(Rest, [9 | Acc]); -tokenise("SEPT"++Rest, Acc) -> tokenise(Rest, [9 | Acc]); -tokenise("SEP"++Rest, Acc) -> tokenise(Rest, [9 | Acc]); -tokenise("OCTOBER"++Rest, Acc) -> tokenise(Rest, [10 | Acc]); -tokenise("OCT"++Rest, Acc) -> tokenise(Rest, [10 | Acc]); -tokenise("NOVEMBER"++Rest, Acc) -> tokenise(Rest, [11 | Acc]); -tokenise("NOVEM"++Rest, Acc) -> tokenise(Rest, [11 | Acc]); -tokenise("NOV"++Rest, Acc) -> tokenise(Rest, [11 | Acc]); -tokenise("DECEMBER"++Rest, Acc) -> tokenise(Rest, [12 | Acc]); -tokenise("DECEM"++Rest, Acc) -> tokenise(Rest, [12 | Acc]); -tokenise("DEC"++Rest, Acc) -> tokenise(Rest, [12 | Acc]); + +%% Worded Months get tagged with ?MONTH_TAG to let the parser know that these +%% are unambiguously declared to be months. This was there's no confusion +%% between, for example: "Aug 12" and "12 Aug" +%% These hint tags are filtered in filter_hints/1 above. +tokenise("JANUARY"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,1} | Acc]); +tokenise("JAN"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,1} | Acc]); +tokenise("FEBRUARY"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,2} | Acc]); +tokenise("FEB"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,2} | Acc]); +tokenise("MARCH"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,3} | Acc]); +tokenise("MAR"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,3} | Acc]); +tokenise("APRIL"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,4} | Acc]); +tokenise("APR"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,4} | Acc]); +tokenise("MAY"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,5} | Acc]); +tokenise("JUNE"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,6} | Acc]); +tokenise("JUN"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,6} | Acc]); +tokenise("JULY"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,7} | Acc]); +tokenise("JUL"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,7} | Acc]); +tokenise("AUGUST"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,8} | Acc]); +tokenise("AUG"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,8} | Acc]); +tokenise("SEPTEMBER"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,9} | Acc]); +tokenise("SEPT"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,9} | Acc]); +tokenise("SEP"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,9} | Acc]); +tokenise("OCTOBER"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,10} | Acc]); +tokenise("OCT"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,10} | Acc]); +tokenise("NOVEMBER"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,11} | Acc]); +tokenise("NOVEM"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,11} | Acc]); +tokenise("NOV"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,11} | Acc]); +tokenise("DECEMBER"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,12} | Acc]); +tokenise("DECEM"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,12} | Acc]); +tokenise("DEC"++Rest, Acc) -> tokenise(Rest, [{?MONTH_TAG,12} | Acc]); tokenise([$: | Rest], Acc) -> tokenise(Rest, [ $: | Acc]); tokenise([$/ | Rest], Acc) -> tokenise(Rest, [ $/ | Acc]); @@ -505,6 +544,10 @@ to_w(X) -> X. suffix(1) -> "st"; suffix(2) -> "nd"; suffix(3) -> "rd"; +suffix(21) -> "st"; +suffix(22) -> "nd"; +suffix(23) -> "rd"; +suffix(31) -> "st"; suffix(_) -> "th". -spec sdayd(date()) -> string(). @@ -672,6 +715,20 @@ basic_parse_test_() -> parse("22 Aug 2008 6:35 PM", ?DATE)), ?_assertEqual({{2008,8,22}, {18,0,0}}, parse("22 Aug 2008 6 PM", ?DATE)), + ?_assertEqual({{2008,8,22}, {18,0,0}}, + parse("Aug 22, 2008 6 PM", ?DATE)), + ?_assertEqual({{2008,8,22}, {18,0,0}}, + parse("August 22nd, 2008 6:00 PM", ?DATE)), + ?_assertEqual({{2008,8,22}, {18,15,15}}, + parse("August 22nd 2008, 6:15:15pm", ?DATE)), + ?_assertEqual({{2008,8,22}, {18,15,15}}, + parse("August 22nd, 2008, 6:15:15pm", ?DATE)), + ?_assertEqual({{2008,8,22}, {18,15,0}}, + parse("Aug 22nd 2008, 18:15", ?DATE)), + ?_assertEqual({{2012,12,10}, {0,0,0}}, + parse("Dec 10th, 2012, 12:00 AM", ?DATE)), + ?_assertEqual({{2012,12,10}, {0,0,0}}, + parse("10 Dec 2012 12:00 AM", ?DATE)), ?_assertEqual({{2001,3,10}, {11,15,0}}, parse("11:15", ?DATE)), ?_assertEqual({{2001,3,10}, {1,15,0}}, From 4ba12ec4ad5ab9fefd5b8dae33912bc14c47adbb Mon Sep 17 00:00:00 2001 From: Jesse Gumm Date: Mon, 18 Feb 2013 14:20:13 -0600 Subject: [PATCH 11/12] Add disambig tests, add disambig parsing date-only Signed-off-by: Jordan Wilberding --- src/ec_date.erl | 64 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/ec_date.erl b/src/ec_date.erl index 69f3446..66ffaeb 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -203,6 +203,21 @@ parse([Hour,$:,Min | PAM], {Date, _Time}, _Opts) when ?is_meridian(PAM) -> parse([Hour | PAM],{Date,_Time}, _Opts) when ?is_meridian(PAM) -> {Date, {hour(Hour,PAM), 0, 0}}; +%% Dates (Any combination with word month "aug 8th, 2008", "8 aug 2008", "2008 aug 21" "2008 5 aug" ) +%% Will work because of the "Hinted month" +parse([Day,Month,Year], {_Date, Time}, _Opts) + when ?is_day(Day) andalso ?is_hinted_month(Month) andalso ?is_year(Year) -> + {{Year, Month, Day}, Time}; +parse([Month,Day,Year], {_Date, Time}, _Opts) + when ?is_day(Day) andalso ?is_hinted_month(Month) andalso ?is_year(Year) -> + {{Year, Month, Day}, Time}; +parse([Year,Day,Month], {_Date, Time}, _Opts) + when ?is_day(Day) andalso ?is_hinted_month(Month) andalso ?is_year(Year) -> + {{Year, Month, Day}, Time}; +parse([Year,Month,Day], {_Date, Time}, _Opts) + when ?is_day(Day) andalso ?is_hinted_month(Month) andalso ?is_year(Year) -> + {{Year, Month, Day}, Time}; + %% Dates 23/april/1963 parse([Day,Month,Year], {_Date, Time}, _Opts) -> {{Year, Month, Day}, Time}; @@ -670,6 +685,11 @@ ltoi(X) -> basic_format_test_() -> [ ?_assertEqual(format("F j, Y, g:i a",?DATE), "March 10, 2001, 5:16 pm"), + ?_assertEqual(format("F jS, Y, g:i a",?DATE), "March 10th, 2001, 5:16 pm"), + ?_assertEqual(format("F jS",{{2011,3,21},{0,0,0}}), "March 21st"), + ?_assertEqual(format("F jS",{{2011,3,22},{0,0,0}}), "March 22nd"), + ?_assertEqual(format("F jS",{{2011,3,23},{0,0,0}}), "March 23rd"), + ?_assertEqual(format("F jS",{{2011,3,31},{0,0,0}}), "March 31st"), ?_assertEqual(format("m.d.y",?DATE), "03.10.01"), ?_assertEqual(format("j, n, Y",?DATE), "10, 3, 2001"), ?_assertEqual(format("Ymd",?DATE), "20010310"), @@ -725,6 +745,50 @@ basic_parse_test_() -> parse("August 22nd, 2008, 6:15:15pm", ?DATE)), ?_assertEqual({{2008,8,22}, {18,15,0}}, parse("Aug 22nd 2008, 18:15", ?DATE)), + ?_assertEqual({{2008,8,2}, {17,16,17}}, + parse("2nd of August 2008", ?DATE)), + ?_assertEqual({{2008,8,2}, {17,16,17}}, + parse("August 2nd, 2008", ?DATE)), + ?_assertEqual({{2008,8,2}, {17,16,17}}, + parse("2nd August, 2008", ?DATE)), + ?_assertEqual({{2008,8,2}, {17,16,17}}, + parse("2008 August 2nd", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,0,0}}, + parse("2-Aug-2008 6 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,0}}, + parse("2-Aug-2008 6:35 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,12}}, + parse("2-Aug-2008 6:35:12 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,0,0}}, + parse("August/2/2008 6 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,0}}, + parse("August/2/2008 6:35 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,0}}, + parse("2 August 2008 6:35 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,0,0}}, + parse("2 Aug 2008 6AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,0}}, + parse("2 Aug 2008 6:35AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,0}}, + parse("2 Aug 2008 6:35 AM", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,0,0}}, + parse("2 Aug 2008 6", ?DATE)), + ?_assertEqual({{2008,8,2}, {6,35,0}}, + parse("2 Aug 2008 6:35", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,35,0}}, + parse("2 Aug 2008 6:35 PM", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,0,0}}, + parse("2 Aug 2008 6 PM", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,0,0}}, + parse("Aug 2, 2008 6 PM", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,0,0}}, + parse("August 2nd, 2008 6:00 PM", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,15,15}}, + parse("August 2nd 2008, 6:15:15pm", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,15,15}}, + parse("August 2nd, 2008, 6:15:15pm", ?DATE)), + ?_assertEqual({{2008,8,2}, {18,15,0}}, + parse("Aug 2nd 2008, 18:15", ?DATE)), ?_assertEqual({{2012,12,10}, {0,0,0}}, parse("Dec 10th, 2012, 12:00 AM", ?DATE)), ?_assertEqual({{2012,12,10}, {0,0,0}}, From e1e30f4a75f46bd9ac4cbb4b6a92f5c79525e308 Mon Sep 17 00:00:00 2001 From: Eric Merritt Date: Thu, 14 Mar 2013 11:50:34 -0700 Subject: [PATCH 12/12] add file type discovery and resolution to ec_file --- src/ec_file.erl | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/ec_file.erl b/src/ec_file.erl index 45adb2a..5e5d353 100644 --- a/src/ec_file.erl +++ b/src/ec_file.erl @@ -15,6 +15,8 @@ mkdir_p/1, find/2, is_symlink/1, + type/1, + real_dir_path/1, remove/1, remove/2, md5sum/1, @@ -115,7 +117,31 @@ is_symlink(Path) -> _ -> false end. - +%% @doc returns the type of the file. +-spec type(file:name()) -> file | symlink | directory. +type(Path) -> + case filelib:is_regular(Path) of + true -> + file; + false -> + case is_symlink(Path) of + true -> + symlink; + false -> + directory + end + end. +%% @doc gets the real path of a directory. This is mostly useful for +%% resolving symlinks. Be aware that this temporarily changes the +%% current working directory to figure out what the actual path +%% is. That means that it can be quite slow. +-spec real_dir_path(file:name()) -> file:name(). +real_dir_path(Path) -> + {ok, CurCwd} = file:get_cwd(), + ok = file:set_cwd(Path), + {ok, RealPath} = file:get_cwd(), + ok = file:set_cwd(CurCwd), + filename:absname(RealPath). %% @doc make a unique temorory directory. Similar function to BSD stdlib %% function of the same name. @@ -334,6 +360,21 @@ exists_test() -> ?assertMatch(true, exists(Name1)), ?assertMatch(false, exists(NoName)). +real_path_test() -> + BaseDir = "foo", + Dir = filename:absname(filename:join(BaseDir, "source1")), + LinkDir = filename:join([BaseDir, "link"]), + ok = mkdir_p(Dir), + file:make_symlink(Dir, LinkDir), + ?assertEqual(Dir, real_dir_path(LinkDir)), + ?assertEqual(directory, type(Dir)), + ?assertEqual(symlink, type(LinkDir)), + TermFile = filename:join(BaseDir, "test_file"), + ok = write_term(TermFile, foo), + ?assertEqual(file, type(TermFile)), + ?assertEqual(true, is_symlink(LinkDir)), + ?assertEqual(false, is_symlink(Dir)). + find_test() -> %% Create a directory in /tmp for the test. Clean everything afterwards {BaseDir, _SourceDir, {Name1, Name2, Name3, _NoName}} = setup_base_and_target(),