diff --git a/Makefile b/Makefile index 5a41b74..af6b493 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ compile : clean : -@rm ebin/* + -@rm erl_crash.dump run : diff --git a/include/ezic.hrl b/include/ezic.hrl index 42aa960..d239578 100644 --- a/include/ezic.hrl +++ b/include/ezic.hrl @@ -19,17 +19,3 @@ % a flattened zone record, giving specific information for a specific time range. -record(flatzone, {tzname, wall_from, wall_to, std_from, std_to, utc_from, utc_to, offset, dstoffset={0,0,0}}). - - - --ifdef(debug). --define(debug(F,X), io:format("{~p:~p} - " ++ F ++ "~n", [?MODULE, ?LINE] ++ X)). --define(debug(F), io:format("{~p:~p} - ~p~n", [?MODULE, ?LINE, F])). --else. --define(debug(F,X), void). --define(debug(F), void). --endif. - - - - diff --git a/src/ezic_date.erl b/src/ezic_date.erl index 3ee9720..f419e30 100644 --- a/src/ezic_date.erl +++ b/src/ezic_date.erl @@ -23,6 +23,7 @@ , add_offset/2 , all_times/3 , m1s/1 + , m1s/3 % comparisons , compare/2 @@ -46,7 +47,7 @@ normalize(Y) when is_integer(Y) -> normalize(Date={Y,M,D}) when is_integer(Y), is_integer(M), is_integer(D) -> {Date, #tztime{}}; -normalize(R={{Y,M,D}, #tztime{}}) -> +normalize(R={{_,_,_}, #tztime{}}) -> R. @@ -104,7 +105,7 @@ add_seconds(Datetime, Seconds) -> calendar:datetime_to_gregorian_seconds(Datetime) + Seconds ) catch - error:Reason -> erlang:error(baddate, Datetime) + error:_ -> erlang:error(baddate, Datetime) end. diff --git a/src/ezic_db.erl b/src/ezic_db.erl index 3db93e4..5adebca 100644 --- a/src/ezic_db.erl +++ b/src/ezic_db.erl @@ -1,6 +1,7 @@ -module(ezic_db). -include("include/ezic.hrl"). -include_lib("stdlib/include/qlc.hrl"). +-include_lib("eunit/include/eunit.hrl"). -export([zones/1, rules/1]). @@ -98,7 +99,7 @@ create_tabs({error, {_, {already_exists,_}}}) -> mnesia:start(), ok; create_tabs(E) -> - ?debug(E). + ?debugVal(E). diff --git a/src/ezic_flatten.erl b/src/ezic_flatten.erl index 5628b35..00a0b4e 100644 --- a/src/ezic_flatten.erl +++ b/src/ezic_flatten.erl @@ -26,7 +26,8 @@ flatten() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% recursively processes sets of similar zones until they're all done, passing zone sets to flatten_zone_set/1 +% recursively processes sets of similar zones until they're all done, +% passing zone sets to flatten_zone_set/1 flatten_zones(Zones) -> flatten_zones(Zones, []). @@ -44,8 +45,9 @@ flatten_zones([Z1|_]= AllZones, Flats) -> -% takes zones one-by-one, gathering relevant rules and creating flat periods of the same gmt offset (#flatzone) -% This is a recursive solution, eliminating Zones until they've been exhausted +% takes zones one-by-one, gathering relevant rules and creating flat +% periods of the same gmt offset (#flatzone). This is a recursive +% solution, eliminating Zones until they've been exhausted flatten_zone_set(Zones) -> flatten_zone_set(?MINFLAT, Zones, []). @@ -57,13 +59,13 @@ flatten_zone_set(Zones) -> % % assumes a new zone every time it is called flatten_zone_set(_, [], Flats) -> - Flats; + Flats; flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} , Zones %[Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | _RestZones], - , Flats) -> + , _Flats) -> - {Zone, RestZones}= ezic_zone:next(Zones, UTCFrom, DSTOffset), - #zone{rule=RuleName, until=UntilTime, gmtoff=Offset}=Zone, + [Zone | _RestZones] = ezic_zone:next(Zones, UTCFrom, DSTOffset), + #zone{rule=RuleName, until=_UntilTime, gmtoff=Offset}=Zone, %% we have a flatzone with start times, we populate the base offset FromTime= FromTimeStub#flatzone{offset=Offset}, @@ -131,7 +133,7 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs ?debugVal(RulesWithDates), [{EndingRuleDate, EndingRule} | _]= lists:sort(RulesWithDates), - {ZoneDate, Zone}= ZoneWithDate, + {ZoneDate, _Zone}= ZoneWithDate, ?debugVal(EndingRuleDate), ?debugVal(ZoneDate), @@ -151,9 +153,9 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs -finish_and_start_flat(FlatStub=#flatzone{utc_from=UTCFrom}, EndingRule=#rule{}, EndingRuleDate={{ERDY,_,_},_}, Offset, DSTOffset) -> +finish_and_start_flat(FlatStub=#flatzone{utc_from=_UTCFrom}, EndingRule=#rule{}, _EndingRuleDate={{ERDY,_,_},_}, Offset, DSTOffset) -> NewFlatStartDates={WD, SD, UD}= ezic_date:for_rule_all(EndingRule, Offset, DSTOffset, ERDY), - FlatEndDates={WDm, SDm, UDm}= ezic_date:m1s(NewFlatStartDates), + _FlatEndDates={WDm, SDm, UDm}= ezic_date:m1s(NewFlatStartDates), EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset), NewFlat1= ?FLAT(WD, SD, UD), diff --git a/src/ezic_rule.erl b/src/ezic_rule.erl index f0db936..f8ff39f 100644 --- a/src/ezic_rule.erl +++ b/src/ezic_rule.erl @@ -26,9 +26,9 @@ parse([Name,FromS,ToS,_Type,InS,OnS,AtS,SaveS,Letters]) -> % 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, DSTOffset, UTCAfter=minimum) -> +project_next(Rule=#rule{from=RFrom}, Offset, DSTOffset, minimum) -> ezic_date:for_rule_utc(Rule, Offset, DSTOffset, RFrom); -project_next(Rule=#rule{from=RFrom, to=RTo}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) -> +project_next(Rule=#rule{}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) -> AllYears= years(Rule), GoodYears= years_after(AY, AllYears), project_next2(Rule, Offset, UTCAfter, DSTOff, GoodYears). @@ -40,16 +40,16 @@ project_next(Rule=#rule{from=RFrom, to=RTo}, Offset, DSTOff, UTCAfter={{AY,_,_}, % returns the sorted list of years a rule existed for. -years(Rule=#rule{from=From, to=only}) -> +years(#rule{from=From, to=only}) -> [From]; -years(Rule=#rule{from=From, to=To}) -> +years(#rule{from=From, to=To}) -> try lists:seq(From, To) catch error:function_clause -> erlang:error(bad_year_range, [From, To]) end. % returns the years from the list that are greater than OR equal to Y -years_after(Y, []) -> +years_after(_, []) -> []; years_after(Y, Years) -> {GOOD, _}= lists:partition( diff --git a/src/ezic_zone.erl b/src/ezic_zone.erl index d7b9247..ebba56b 100644 --- a/src/ezic_zone.erl +++ b/src/ezic_zone.erl @@ -46,10 +46,12 @@ current_as_of_utc(UTCDatetime, TzName) -> - +%% returns the active zone for a given utc time +%% note: requires that flattening be done +%% this will likely get reworked significantly get_zone_utc(_, []) -> erlang:error(no_current); -get_zone_utc(_UTCDatetime, Zones) -> +get_zone_utc(_UTCDatetime, _Zones) -> not_done. @@ -95,13 +97,16 @@ project_end_utc(Zone=#zone{}, DSTOffset) -> % Note that dst differences *can* change which zone comes next, % though it's very unlikely (and does not exist in the current tz database files). % this method covers that event, anyhow. + +%% BAD!!! UCTFrom is not used! next(ZoneList, UTCFrom, DSTOff) -> DatedList= lists:map( fun(Z=#zone{until=Until, gmtoff=Offset})-> - NU= ezic_date:normalize(Until), - {_,_,UTCDt}= ezic_date:all_times(NU, Offset, DSTOff), + NUntil= ezic_date:normalize(Until), + {_,_,UTCDt}= ezic_date:all_times(NUntil, Offset, DSTOff), {UTCDt, Z} end , ZoneList), - [{_,Zone} | DRest]= lists:sort(DatedList), - {Zone, [R || {_,R}<- DRest]}. + FilteredList= lists:filter(fun({IDt,_})-> UTCFrom =< IDt end, DatedList), + SortedList= lists:sort(FilteredList), + [Z || {_,Z}<- SortedList]. diff --git a/test/ezic_zone_tests.erl b/test/ezic_zone_tests.erl index 2b5bd39..39789e1 100644 --- a/test/ezic_zone_tests.erl +++ b/test/ezic_zone_tests.erl @@ -32,3 +32,58 @@ project_end_utc_test_() -> #zone{until={{2010,11,10},#tztime{time={16,20,0}, flag=u}}, gmtoff={-7,0,0}} , {1,0,0})) ]. + + + +next_datetime_test_() -> + BZ=#zone{gmtoff={0,0,0}}, + AllZones= [ + Z1=BZ#zone{until={{2010,11,15},#tztime{}}} + , Z2=BZ#zone{until={{2010,11,5},#tztime{}}} + , Z3=BZ#zone{until={{2010,11,25},#tztime{}}} + + , Zt1=BZ#zone{until={{2010,11,5},#tztime{time={5,0,0}, flag=u}}} + , Zt2=BZ#zone{until={{2010,11,5},#tztime{time={12,0,0}, flag=u}}} + , Zt3=BZ#zone{until={{2010,11,5},#tztime{time={17,0,0}, flag=u}}} + + , Zy1=BZ#zone{until={{1978,11,5},#tztime{}}} + , Zy2=BZ#zone{until={{1979,11,5},#tztime{}}} + , Zy3=BZ#zone{until={{1980,11,5},#tztime{}}} + ], + [ + % day limits + ?_assertMatch([Z2|_], ezic_zone:next(AllZones, {{2010,11,1},{0,0,0}}, {0,0,0})) + , ?_assertMatch([Z1|_], ezic_zone:next(AllZones, {{2010,11,6},{0,0,0}}, {0,0,0})) + , ?_assertMatch([Z3|_], ezic_zone:next(AllZones, {{2010,11,16},{0,0,0}}, {0,0,0})) + + % time limits + , ?_assertMatch([Zt1|_], ezic_zone:next(AllZones, {{2010,11,5},{0,0,1}}, {0,0,0})) + , ?_assertMatch([Zt2|_], ezic_zone:next(AllZones, {{2010,11,5},{5,0,1}}, {0,0,0})) + , ?_assertMatch([Zt3|_], ezic_zone:next(AllZones, {{2010,11,5},{12,0,1}}, {0,0,0})) + + % year tests + , ?_assertMatch([Zy1|_], ezic_zone:next(AllZones, {{1978,11,5},{0,0,0}}, {0,0,0})) + , ?_assertMatch([Zy2|_], ezic_zone:next(AllZones, {{1978,11,7},{0,0,0}}, {0,0,0})) + , ?_assertMatch([Zy3|_], ezic_zone:next(AllZones, {{1979,11,8},{0,0,0}}, {0,0,0})) + + ]. + +%% Take 3 zones identical aside from their "time-relativity", each +%% ending in standard, wall, and universal time, the following tests +%% show the choice of "next soonest" as deterministic. +next_relative_time_test_() -> + Zones= [ + ZW=#zone{until={{2010,11,7},#tztime{time={2,0,0}, flag=w}}, gmtoff={-7,0,0}} + , ZS=#zone{until={{2010,11,7},#tztime{time={2,0,0}, flag=s}}, gmtoff={-7,0,0}} + , ZU=#zone{until={{2010,11,7},#tztime{time={2,0,0}, flag=u}}, gmtoff={-7,0,0}} + ], + [ + %% same dst, different from + ?_assertMatch([ZW|_], ezic_zone:next(Zones,{{2010,11,7},{6,0,0}},{2,0,0})) + , ?_assertMatch([ZS|_], ezic_zone:next(Zones,{{2010,11,7},{7,0,1}},{2,0,0})) + , ?_assertMatch([ZU|_], ezic_zone:next(Zones,{{2010,11,7},{1,0,1}},{2,0,0})) + + %% same from, different dst + , ?_assertMatch([ZW|_], ezic_zone:next(Zones,{{2010,11,7},{8,50,0}},{0,9,0})) %1:59wt, 1:50st + , ?_assertMatch([ZS|_], ezic_zone:next(Zones,{{2010,11,7},{8,50,0}},{0,-1,0})) %1:49wt, 1:50st + ].