From 6d17415cdd3ae1e3ff74f84adbfc2f3a92842dc0 Mon Sep 17 00:00:00 2001 From: Eric Merritt Date: Mon, 17 Oct 2011 16:08:13 -0500 Subject: [PATCH] support US style date parsing using the same defaults from php as the original Signed-off-by: Jordan Wilberding --- src/ec_date.erl | 80 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 64 insertions(+), 16 deletions(-) diff --git a/src/ec_date.erl b/src/ec_date.erl index afe83db..6662dd6 100644 --- a/src/ec_date.erl +++ b/src/ec_date.erl @@ -3,10 +3,17 @@ %% %% Licensed under the MIT license %% -%% This module formats erlang dates in the form -%% {{Year, Month, Day}, {Hour, Minute, Second}} -%% to printable strings, using (almost) equivalent -%% formatting rules as http://uk.php.net/date +%% This module formats erlang dates in the form {{Year, Month, Day}, +%% {Hour, Minute, Second}} to printable strings, using (almost) +%% equivalent formatting rules as http://uk.php.net/date, US vs +%% European dates are disambiguated in the same way as +%% http://uk.php.net/manual/en/function.strtotime.php That is, Dates +%% in the m/d/y or d-m-y formats are disambiguated by looking at the +%% separator between the various components: if the separator is a +%% slash (/), then the American m/d/y is assumed; whereas if the +%% separator is a dash (-) or a dot (.), then the European d-m-y +%% format is assumed. To avoid potential ambiguity, it's best to use +%% ISO 8601 (YYYY-MM-DD) dates. %% %% erlang has no concept of timezone so the following %% formats are not implemented: B e I O P T Z @@ -24,7 +31,8 @@ %% defines make sense -define( is_num(X), (X >= $0 andalso X =< $9) ). -define( is_meridian(X), (X==[] orelse X==[am] orelse X==[pm]) ). --define( is_sep(X), (X==$- orelse X==$/) ). +-define( is_us_sep(X), ( X==$/) ). +-define( is_world_sep(X), ( X==$-) ). -define(GREGORIAN_SECONDS_1970, 62167219200). @@ -105,17 +113,41 @@ parse([Hour,$:,Min | PAM], {Date, _Time}, _Opts) when ?is_meridian(PAM) -> %% Dates 23/april/1963 parse([Day,Month,Year], {_Date, Time}, _Opts) -> {{Year, Month, Day}, Time}; -parse([Day,X,Month,X,Year], {_Date, Time}, _Opts) when ?is_sep(X) -> +parse([Year,X,Month,X,Day], {_Date, Time}, _Opts) + when (?is_us_sep(X) orelse ?is_world_sep(X)) + andalso Year > 31 -> + {{Year, Month, Day}, Time}; +parse([Month,X,Day,X,Year], {_Date, Time}, _Opts) when ?is_us_sep(X) -> + {{Year, Month, Day}, Time}; +parse([Day,X,Month,X,Year], {_Date, Time}, _Opts) when ?is_world_sep(X) -> {{Year, Month, Day}, Time}; %% Date/Times 22 Aug 2008 6:35 PM +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 -> + {{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_sep(X) -> + when ?is_meridian(PAM) andalso ?is_world_sep(X) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, 0}}; +parse([Month,X,Day,X,Year,Hour,$:,Min | PAM], _Date, _Opts) + when ?is_meridian(PAM) andalso ?is_us_sep(X) -> + {{Year, Month, Day}, {hour(Hour, PAM), Min, 0}}; + +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 -> + {{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) -> + {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}}; parse([Day,X,Month,X,Year,Hour,$:,Min,$:,Sec | PAM], _Now, _Opts) - when ?is_meridian(PAM) andalso ?is_sep(X) -> + when ?is_meridian(PAM) andalso ?is_world_sep(X) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, Sec}}; + parse([Day,Month,Year,Hour,$:,Min | PAM], _Now, _Opts) when ?is_meridian(PAM) -> {{Year, Month, Day}, {hour(Hour, PAM), Min, 0}}; @@ -492,9 +524,7 @@ basic_parse_test_() -> ?_assertEqual({{2008,8,22}, {6,35,12}}, parse("22-Aug-2008 6:35:12 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}}, - parse("22/Aug/2008 6:35 AM", ?DATE)), - ?_assertEqual({{2008,8,22}, {6,35,0}}, - parse("22/August/2008 6:35 AM", ?DATE)), + parse("August/22/2008 6:35 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}}, parse("22 August 2008 6:35 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}}, @@ -514,11 +544,29 @@ basic_parse_test_() -> ?_assertEqual({{2001,3,10}, {3,45,39}}, parse("3:45:39", ?DATE)), ?_assertEqual({{1963,4,23}, {17,16,17}}, - parse("23/4/1963", ?DATE)), + parse("23-4-1963", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("23-april-1963", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("23-apr-1963", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("4/23/1963", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("april/23/1963", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("apr/23/1963", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("1963/4/23", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("1963/april/23", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("1963/apr/23", ?DATE)), + ?_assertEqual({{1963,4,23}, {17,16,17}}, + parse("1963-4-23", ?DATE)), ?_assertEqual({{1963,4,23}, {17,16,17}}, - parse("23/april/1963", ?DATE)), + parse("1963-4-23", ?DATE)), ?_assertEqual({{1963,4,23}, {17,16,17}}, - parse("23/apr/1963", ?DATE)), + parse("1963-apr-23", ?DATE)), ?_assertThrow({?MODULE, {bad_date, "23/ap/195"}}, parse("23/ap/195", ?DATE)), ?_assertEqual({{2001,3,10}, {6,45,0}}, @@ -538,9 +586,9 @@ parse_with_days_test_() -> ?_assertEqual({{2008,8,22}, {6,35,12}}, parse("Sunday 22-Aug-2008 6:35:12 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}}, - parse("Sun 22/Aug/2008 6:35 AM", ?DATE)), + parse("Sun 22-Aug-2008 6:35 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}}, - parse("THURSDAY, 22/August/2008 6:35 AM", ?DATE)), + parse("THURSDAY, 22-August-2008 6:35 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}}, parse("THU 22 August 2008 6:35 AM", ?DATE)), ?_assertEqual({{2008,8,22}, {6,35,0}},