removed all warnings. fixed bug in ezic_zone:next/3. unit tests for ezic_zone:next/3
ezic_zone:next/3 was not filtering the Zones to only those after UTCFrom. This may have caused an endless loop, always returning the earliest zone regardless of the progress of time. I wont know; I caught it rather early.
This commit is contained in:
1
Makefile
1
Makefile
@@ -23,6 +23,7 @@ compile :
|
|||||||
|
|
||||||
clean :
|
clean :
|
||||||
-@rm ebin/*
|
-@rm ebin/*
|
||||||
|
-@rm erl_crash.dump
|
||||||
|
|
||||||
|
|
||||||
run :
|
run :
|
||||||
|
|||||||
@@ -19,17 +19,3 @@
|
|||||||
% a flattened zone record, giving specific information for a specific time range.
|
% 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}}).
|
-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.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
, add_offset/2
|
, add_offset/2
|
||||||
, all_times/3
|
, all_times/3
|
||||||
, m1s/1
|
, m1s/1
|
||||||
|
, m1s/3
|
||||||
|
|
||||||
% comparisons
|
% comparisons
|
||||||
, compare/2
|
, compare/2
|
||||||
@@ -46,7 +47,7 @@ normalize(Y) when is_integer(Y) ->
|
|||||||
normalize(Date={Y,M,D})
|
normalize(Date={Y,M,D})
|
||||||
when is_integer(Y), is_integer(M), is_integer(D) ->
|
when is_integer(Y), is_integer(M), is_integer(D) ->
|
||||||
{Date, #tztime{}};
|
{Date, #tztime{}};
|
||||||
normalize(R={{Y,M,D}, #tztime{}}) ->
|
normalize(R={{_,_,_}, #tztime{}}) ->
|
||||||
R.
|
R.
|
||||||
|
|
||||||
|
|
||||||
@@ -104,7 +105,7 @@ add_seconds(Datetime, Seconds) ->
|
|||||||
calendar:datetime_to_gregorian_seconds(Datetime) + Seconds
|
calendar:datetime_to_gregorian_seconds(Datetime) + Seconds
|
||||||
)
|
)
|
||||||
catch
|
catch
|
||||||
error:Reason -> erlang:error(baddate, Datetime)
|
error:_ -> erlang:error(baddate, Datetime)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
-module(ezic_db).
|
-module(ezic_db).
|
||||||
-include("include/ezic.hrl").
|
-include("include/ezic.hrl").
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
|
|
||||||
-export([zones/1, rules/1]).
|
-export([zones/1, rules/1]).
|
||||||
@@ -98,7 +99,7 @@ create_tabs({error, {_, {already_exists,_}}}) ->
|
|||||||
mnesia:start(),
|
mnesia:start(),
|
||||||
ok;
|
ok;
|
||||||
create_tabs(E) ->
|
create_tabs(E) ->
|
||||||
?debug(E).
|
?debugVal(E).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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) ->
|
||||||
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)
|
% takes zones one-by-one, gathering relevant rules and creating flat
|
||||||
% This is a recursive solution, eliminating Zones until they've been exhausted
|
% 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(Zones) ->
|
||||||
flatten_zone_set(?MINFLAT, Zones, []).
|
flatten_zone_set(?MINFLAT, Zones, []).
|
||||||
|
|
||||||
@@ -57,13 +59,13 @@ flatten_zone_set(Zones) ->
|
|||||||
%
|
%
|
||||||
% assumes a new zone every time it is called
|
% assumes a new zone every time it is called
|
||||||
flatten_zone_set(_, [], Flats) ->
|
flatten_zone_set(_, [], Flats) ->
|
||||||
Flats;
|
Flats;
|
||||||
flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
|
flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
|
||||||
, Zones %[Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | _RestZones],
|
, Zones %[Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | _RestZones],
|
||||||
, Flats) ->
|
, _Flats) ->
|
||||||
|
|
||||||
{Zone, RestZones}= ezic_zone:next(Zones, UTCFrom, DSTOffset),
|
[Zone | _RestZones] = ezic_zone:next(Zones, UTCFrom, DSTOffset),
|
||||||
#zone{rule=RuleName, until=UntilTime, gmtoff=Offset}=Zone,
|
#zone{rule=RuleName, until=_UntilTime, gmtoff=Offset}=Zone,
|
||||||
|
|
||||||
%% we have a flatzone with start times, we populate the base offset
|
%% we have a flatzone with start times, we populate the base offset
|
||||||
FromTime= FromTimeStub#flatzone{offset=Offset},
|
FromTime= FromTimeStub#flatzone{offset=Offset},
|
||||||
@@ -131,7 +133,7 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs
|
|||||||
?debugVal(RulesWithDates),
|
?debugVal(RulesWithDates),
|
||||||
|
|
||||||
[{EndingRuleDate, EndingRule} | _]= lists:sort(RulesWithDates),
|
[{EndingRuleDate, EndingRule} | _]= lists:sort(RulesWithDates),
|
||||||
{ZoneDate, Zone}= ZoneWithDate,
|
{ZoneDate, _Zone}= ZoneWithDate,
|
||||||
|
|
||||||
?debugVal(EndingRuleDate),
|
?debugVal(EndingRuleDate),
|
||||||
?debugVal(ZoneDate),
|
?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),
|
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),
|
EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset),
|
||||||
NewFlat1= ?FLAT(WD, SD, UD),
|
NewFlat1= ?FLAT(WD, SD, UD),
|
||||||
|
|||||||
@@ -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,
|
% 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.
|
% 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);
|
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),
|
AllYears= years(Rule),
|
||||||
GoodYears= years_after(AY, AllYears),
|
GoodYears= years_after(AY, AllYears),
|
||||||
project_next2(Rule, Offset, UTCAfter, DSTOff, GoodYears).
|
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.
|
% returns the sorted list of years a rule existed for.
|
||||||
years(Rule=#rule{from=From, to=only}) ->
|
years(#rule{from=From, to=only}) ->
|
||||||
[From];
|
[From];
|
||||||
years(Rule=#rule{from=From, to=To}) ->
|
years(#rule{from=From, to=To}) ->
|
||||||
try lists:seq(From, To)
|
try lists:seq(From, To)
|
||||||
catch error:function_clause ->
|
catch error:function_clause ->
|
||||||
erlang:error(bad_year_range, [From, To])
|
erlang:error(bad_year_range, [From, To])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
% returns the years from the list that are greater than OR equal to Y
|
% returns the years from the list that are greater than OR equal to Y
|
||||||
years_after(Y, []) ->
|
years_after(_, []) ->
|
||||||
[];
|
[];
|
||||||
years_after(Y, Years) ->
|
years_after(Y, Years) ->
|
||||||
{GOOD, _}= lists:partition(
|
{GOOD, _}= lists:partition(
|
||||||
|
|||||||
@@ -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(_, []) ->
|
get_zone_utc(_, []) ->
|
||||||
erlang:error(no_current);
|
erlang:error(no_current);
|
||||||
get_zone_utc(_UTCDatetime, Zones) ->
|
get_zone_utc(_UTCDatetime, _Zones) ->
|
||||||
not_done.
|
not_done.
|
||||||
|
|
||||||
|
|
||||||
@@ -95,13 +97,16 @@ project_end_utc(Zone=#zone{}, DSTOffset) ->
|
|||||||
% Note that dst differences *can* change which zone comes next,
|
% 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).
|
% though it's very unlikely (and does not exist in the current tz database files).
|
||||||
% this method covers that event, anyhow.
|
% this method covers that event, anyhow.
|
||||||
|
|
||||||
|
%% BAD!!! UCTFrom is not used!
|
||||||
next(ZoneList, UTCFrom, DSTOff) ->
|
next(ZoneList, UTCFrom, DSTOff) ->
|
||||||
DatedList= lists:map(
|
DatedList= lists:map(
|
||||||
fun(Z=#zone{until=Until, gmtoff=Offset})->
|
fun(Z=#zone{until=Until, gmtoff=Offset})->
|
||||||
NU= ezic_date:normalize(Until),
|
NUntil= ezic_date:normalize(Until),
|
||||||
{_,_,UTCDt}= ezic_date:all_times(NU, Offset, DSTOff),
|
{_,_,UTCDt}= ezic_date:all_times(NUntil, Offset, DSTOff),
|
||||||
{UTCDt, Z}
|
{UTCDt, Z}
|
||||||
end
|
end
|
||||||
, ZoneList),
|
, ZoneList),
|
||||||
[{_,Zone} | DRest]= lists:sort(DatedList),
|
FilteredList= lists:filter(fun({IDt,_})-> UTCFrom =< IDt end, DatedList),
|
||||||
{Zone, [R || {_,R}<- DRest]}.
|
SortedList= lists:sort(FilteredList),
|
||||||
|
[Z || {_,Z}<- SortedList].
|
||||||
|
|||||||
@@ -32,3 +32,58 @@ project_end_utc_test_() ->
|
|||||||
#zone{until={{2010,11,10},#tztime{time={16,20,0}, flag=u}}, gmtoff={-7,0,0}}
|
#zone{until={{2010,11,10},#tztime{time={16,20,0}, flag=u}}, gmtoff={-7,0,0}}
|
||||||
, {1,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
|
||||||
|
].
|
||||||
|
|||||||
Reference in New Issue
Block a user