Add relative date/time parsing parser.

This commit is contained in:
Jesse Gumm 2016-03-05 15:16:03 -06:00
parent 5d73286e92
commit 41b313d425

View file

@ -61,6 +61,8 @@
add_months/1,
add_years/2,
add_years/1,
add_unit/2,
add_unit/3,
add_date/2
]).
@ -94,6 +96,9 @@
clear_timezone/1
]).
-export([
parse_relative/1
]).
%% Exported for API compatibility with ec_date
-export([
@ -309,7 +314,8 @@ to_unixtime(Date) ->
to_unixtime(_, Unixtime) when is_integer(Unixtime) ->
Unixtime;
to_unixtime(_, {MegaSecs,Secs,_}) ->
to_unixtime(_, {MegaSecs,Secs,_}) when is_integer(MegaSecs), is_integer(Secs) ->
MegaSecs*1000000 + Secs;
to_unixtime(Disamb, ToParse) ->
%% We want to treat all unixtimes as GMT
@ -483,6 +489,39 @@ add_years(Years, Date) ->
add_years(Years) ->
add_years(Years, os:timestamp()).
add_unit(second, Value, Date) ->
add_unit(seconds, Value, Date);
add_unit(seconds, Value, Date) ->
add_seconds(Value, Date);
add_unit(minute, Value, Date) ->
add_unit(minutes, Value, Date);
add_unit(minutes, Value, Date) ->
add_minutes(Value, Date);
add_unit(hour, Value, Date) ->
add_unit(hours, Value, Date);
add_unit(hours, Value, Date) ->
add_hours(Value, Date);
add_unit(day, Value, Date) ->
add_unit(days, Value, Date);
add_unit(days, Value, Date) ->
add_days(Value, Date);
add_unit(week, Value, Date) ->
add_unit(weeks, Value, Date);
add_unit(weeks, Value, Date) ->
add_weeks(Value, Date);
add_unit(month, Value, Date) ->
add_unit(months, Value, Date);
add_unit(months, Value, Date) ->
add_months(Value, Date);
add_unit(year, Value, Date) ->
add_unit(years, Value, Date);
add_unit(years, Value, Date) ->
add_years(Value, Date).
add_unit(Unit, Value) ->
add_unit(Unit, Value, os:timestamp()).
add_date({{AddY, AddM, AddD}, {AddH, AddI, AddS}}, Date) ->
{{Y, M, D}, {H, I, S}} = to_date(Date),
Date1 = fix_maybe_improper_date({{Y+AddY, M+AddM, D+AddD}, {H, I, S}}),
@ -610,6 +649,58 @@ range_years(Interval, Start, Finish) ->
range(years, Interval, Start, Finish).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%% Relative Date Parsing %%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
parse_relative({relative, Date, Relation}) when is_atom(Relation) ->
parse_relative({relative, Date, atom_to_list(Relation)});
parse_relative({relative, Date, Relation}) ->
{OpStr, NumStr, UnitStr} = parse_actual_relation(Relation),
{Num, Unit} = normalize_relative_matches(OpStr, NumStr, UnitStr),
add_unit(Unit, Num, Date);
parse_relative(now) ->
unixtime();
parse_relative("now") ->
unixtime();
parse_relative(<<"now">>) ->
unixtime();
parse_relative(Relation) ->
parse_relative({relative, unixtime(), Relation}).
%% I would do this function recursively, but the return order of arguments
%% inconsistent, so I just leave it like this. It's a little nasty to have the
%% nested case expressions, but I can deal with it.
parse_actual_relation(Relation) ->
PrefixRE = "^(\\-|\\+|in)\\s?(\\d+) (second|minute|hour|day|week|month|year)s?$",
SuffixRE = "^(\\d+) (second|minute|hour|day|week|month|year)s\\s?(ago|from now)?$",
case re:run(Relation, PrefixRE, [{capture, all_but_first, list}]) of
nomatch ->
case re:run(Relation, SuffixRE, [{capture, all_but_first, list}]) of
nomatch -> undefined;
{match, [NumStr, UnitStr, OpStr]} ->
{OpStr, NumStr, UnitStr}
end;
{match, [OpStr, NumStr, UnitStr]} ->
{OpStr, NumStr, UnitStr}
end.
normalize_relative_matches(OpStr, NumStr, UnitStr) ->
Op = normalize_relative_op(OpStr),
Num = list_to_integer(Op ++ NumStr),
Unit = list_to_existing_atom(UnitStr),
{Num, Unit}.
normalize_relative_op(Op) ->
case Op of
"+" -> "+";
"-" -> "-";
"ago" -> "-";
"from now" -> "+";
"in" -> "+"
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Timezone Stuff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@ -637,6 +728,8 @@ extract_timezone(DateString) when is_list(DateString) ->
end;
extract_timezone(Date={{_,_,_},{_,_,_}}) ->
{Date, ?DETERMINE_TZ};
extract_timezone(Rel={relative, _, _}) ->
{Rel, "GMT"};
extract_timezone(Now={_,_,_}) ->
{Now, "GMT"};
extract_timezone({MiscDate,TZ}) ->