datetime manipulation is now much clearer. tests were added, and expectations stated explicitly in many places. rules got lots of attention.

This commit is contained in:
aj
2010-11-10 01:52:13 -08:00
parent 216a8d9ff8
commit d84a0c1822
11 changed files with 245 additions and 116 deletions

View File

@@ -3,8 +3,7 @@
-export([parse/1]).
-export([next_event/4]).
-export([project_next/4]).
parse([Name,FromS,ToS,_Type,InS,OnS,AtS,SaveS,Letters]) ->
@@ -25,37 +24,38 @@ parse([Name,FromS,ToS,_Type,InS,OnS,AtS,SaveS,Letters]) ->
% determines the next zone or dst change event
% returns the latest possible times the previous period is in effect (`Until -1sec` in most cases)
% Rules are sorted
next_event(_BaseFlat, ZoneEnd, Offset, []) ->
{end_until, make_times(ZoneEnd, Offset)};
% BaseFlat has offset defined
% @todo ensure offset defined on BaseFlat
% Rules are sorted in ascending order (oldest first)
next_event(_BaseFlat=#flatzone{utc_from=#tztime{time={{_Year,_,_},_}}},
_ZoneEnd, _ZoneOffset, [_Rule|_RestRules]) ->
not_done.
% returns the projected date of the next "rule event" in UTC time,
% after the given UTCDatetime, with the given the zone Offset and current DST offset.
project_next(Rule=#rule{from=RFrom, to=RTo}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) ->
AllYears= years(Rule),
GoodYears= years_after(AY, AllYears),
project_next2(Rule, Offset, UTCAfter, DSTOff, GoodYears).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PRIVATE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% create endtimes for a #flatzone, with no dst offset
make_times(Until, Offset) ->
make_times(Until, Offset, 0).
make_times(Until, Offset, DSTOffset) ->
ezic_date:all_times(Until, Offset, DSTOffset).
% returns the sorted list of years a rule existed for.
years(Rule=#rule{from=From, to=only}) ->
[From];
years(Rule=#rule{from=From, to=To}) ->
try lists:seq(From, To)
catch error:function_clause ->
erlang:error(bad_year_range)
end.
% returns the years from the list that are greater than OR equal to Y
years_after(Y, []) ->
[];
years_after(Y, Years) ->
{GOOD, _}= lists:partition(
fun(X) when Y =< X -> true;
(_) -> false
end, Years),
GOOD.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INTERNAL
@@ -63,4 +63,45 @@ make_times(Until, Offset, DSTOffset) ->
project_next2(_,_,_,_,[]) ->
none;
project_next2(Rule, Offset, UTCAfter, DSTOffset, [Year|RestYears]) ->
RuleDate= ezic_date:for_rule_utc(Rule, Offset, DSTOffset, Year),
case ezic_date:compare(UTCAfter, RuleDate) andalso (not ezic_date:equal(UTCAfter, RuleDate)) of
true -> RuleDate;
_ -> project_next2(Rule, Offset, UTCAfter, DSTOffset, RestYears)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Tests of Private Methods
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
years_test_() ->
[
?_assertEqual([1952], years(#rule{from=1952,to=only}))
, ?_assertEqual([1952,1953], years(#rule{from=1952,to=1953}))
, ?_assertEqual([1952,1953,1954,1955], years(#rule{from=1952,to=1955}))
, ?_assertError(bad_year_range, years(#rule{from=1955,to=1952}))
].
% @todo type checking. lack of types worries me sometimes :(
years_after_test_() ->
[
?_assertEqual([2010], years_after(2010, [2010]))
, ?_assertEqual([2010,2011], years_after(2010, [2010,2011]))
, ?_assertEqual([2011], years_after(2011, [2010,2011]))
, ?_assertEqual([], years_after(2012, [2010,2011]))
, ?_assertEqual([], years_after(2012, []))
].
-endif.