From 41b313d425883095fc8e41bf971452eefe87b2b7 Mon Sep 17 00:00:00 2001 From: Jesse Gumm Date: Sat, 5 Mar 2016 15:16:03 -0600 Subject: [PATCH] Add relative date/time parsing parser. --- src/qdate.erl | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/src/qdate.erl b/src/qdate.erl index d43f637..8f9c5ea 100644 --- a/src/qdate.erl +++ b/src/qdate.erl @@ -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}) ->