handled case where zone and rule both end. dealing with Tripoli recursion bug (ambiguous year in ezic_date:for_rule)

This commit is contained in:
aj
2010-11-13 03:25:50 -08:00
parent a7b0adcbce
commit 3f438e66e0
8 changed files with 185 additions and 108 deletions

View File

@@ -9,16 +9,22 @@
%% @todo move MAXYEAR to config file
-define(MAXYEAR, 2500). % last year to process flatzones for.
-export([flatten/0]).
%% debug
-export([flatten_all_zones/1]).
flatten() ->
AllZones= ezic_db:get_all(zone),
flatten_all_zones(AllZones),
not_done.
done.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -28,15 +34,15 @@ flatten() ->
% recursively processes sets of similar zones until they're all done,
% passing zone sets to flatten_zone_set/1
flatten_all_zones(Zones) ->
flatten_all_zones(Zones, []).
flatten_all_zones([], Flats) ->
Flats;
flatten_all_zones([Z1|_]= AllZones, Flats) ->
flatten_all_zones([]) ->
done;
flatten_all_zones([Z1|_]= AllZones) ->
{CurrentZones, RestZones}= ezic_zone:split_by_name(Z1, AllZones),
NewFlats= lists:merge(Flats, flatten_zone_set(CurrentZones)),
flatten_all_zones(RestZones, NewFlats).
Flats= flatten_zone_set(CurrentZones),
ezic_db:insert_all(Flats),
flatten_all_zones(RestZones).
@@ -50,7 +56,7 @@ flatten_all_zones([Z1|_]= AllZones, Flats) ->
%% offset (#flatzone). This is a recursive solution, eliminating Zones
%% from the list until it's been exhausted
flatten_zone_set(Zones) ->
flatten_zone_set(?MINFLAT, Zones, []).
flatten_zone_set(?MINFLAT, Zones, [], none).
% flatten_zone_set(FromTime, Zones, Flats) -> [#flatzone{}]
@@ -59,12 +65,10 @@ flatten_zone_set(Zones) ->
% Flats= [#flatzone{}]
%
% assumes a new zone every time it is called
flatten_zone_set(_, [], Flats) ->
erlang:error(debug_quit),
Flats;
flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
, Zones %[Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | _RestZones],
, Flats) ->
, Flats
, CurrentRule) ->
[Zone | RestZones] = ezic_zone:next(Zones, UTCFrom, DSTOffset),
#zone{rule=RuleName, until=_UntilTime, gmtoff=Offset}= Zone,
@@ -75,11 +79,11 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
%% must populate the base gmt offset and name for the current zone
FromTime= populate_flatzone(FromTimeStub, Zone),
% FromTime= FromTimeStub#flatzone{offset=Offset, tzname=Zone#zone.name},
%% if this is the first run, DST offset is {0,0,0}
%% if this is a recursion, DST offset is the previous zone's last DST offset
%% @todo see if dst rules carry over zone changes IRL
%% if this is the first run, DST offset is {0,0,0} if this is a
%% recursion, DST offset is the previous zone's last DST offset
%% @todo see if dst rules carry over zone changes IRL. I'll assume
%% they don't.
%% we gather all rules that _may_ apply
@@ -89,35 +93,36 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
?debugVal(FromTime),
?debugVal(Rules),
%% apply all rules in order, creating flatzones, until this zone
%% ends, then regain control.
{RuleFlats, LastFlat, EndingDST}= flatten_rule_set(FromTime, Zone, Rules, []),
%% ends, then regain control. rules have been exhausted, and zone
%% is ending. let's finish this.
?debugVal(RuleFlats),
?debugVal(EndingDST),
%% rules have been exhausted, and zone is ending. let's finish this.
{FinalFlat, NextFlat}= finish_and_start_flat(LastFlat, Zone, EndingDST),
FinalFlats= lists:merge([[FinalFlat], RuleFlats, Flats]),
{RuleFlats, NextFlat, EndingRule}= flatten_rule_set(FromTime, Zone, Rules, CurrentRule, []),
FinalFlats= lists:append([RuleFlats, Flats]),
?debugVal(FinalFlat),
?debugVal(FinalFlats),
?debugVal(NextFlat),
flatten_zone_set(NextFlat, RestZones, FinalFlats).
%% return flats if we've exceeded our years, or recurse if we can keep going
NFUTCFrom = NextFlat#flatzone.utc_from,
case maxyear_reached(NFUTCFrom) of
true ->
?debugMsg("maxyear reached from flatten_zone_set"),
FinalFlats;
false ->
flatten_zone_set(NextFlat, RestZones, FinalFlats, EndingRule)
end.
flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offset=Offset}
, Zone, Rules, Flats) ->
, Zone, Rules, CurrentRule, Flats) ->
%% add normalized (possibly inaccurate) dates for sorting
%% purposes. note this may be empty. also note this MUST (I think)
%% be done in the loop, since UTCFrom and DSTOffset can
%% potentially change which rule comes next (however unlikely that
%% case may be in real life)
ValidRules= lists:delete(CurrentRule, Rules),
RulesWithDates= lists:foldl(
fun(R, Acc)->
case ezic_rule:project_next(R, Offset, DSTOffset, UTCFrom) of
@@ -125,12 +130,10 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs
D -> [{D,R} | Acc]
end
end
,[], Rules),
,[], ValidRules),
?debugVal(RulesWithDates),
{ActualEndingRuleDate, EndingRule}=
{UTCEndingRuleDate, EndingRule}=
case length(RulesWithDates) > 0 of
false -> {maximum, none};
true -> hd(lists:sort(RulesWithDates))
@@ -139,26 +142,45 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs
ZoneDate= ezic_zone:project_end_utc(Zone, DSTOffset),
?debugVal(ActualEndingRuleDate),
?debugVal(UTCEndingRuleDate),
?debugVal(ZoneDate),
case ezic_date:compare(ActualEndingRuleDate, ZoneDate) of
case maxyear_reached(UTCEndingRuleDate) andalso maxyear_reached(ZoneDate) of
true ->
%% same zone, new rule
{EndFlat, NextFlat}= finish_and_start_flat(FlatStart, EndingRule, ActualEndingRuleDate, Offset, DSTOffset),
NewFlats= [EndFlat | Flats],
flatten_rule_set(NextFlat, Zone, Rules, NewFlats);
?debugMsg("maxyear reached from flatten_rule_set"),
{Flats, #flatzone{utc_from=maximum}, none};
false ->
%% new zone is handled in the caller: flatten_zone_set
{Flats, FlatStart, DSTOffset}
case ezic_date:compare(ZoneDate, UTCEndingRuleDate) of
false ->
%% same zone, new rule
{EndFlat, NextFlat}= finish_and_start_flat(FlatStart, EndingRule, UTCEndingRuleDate),
NewFlats= [EndFlat | Flats],
flatten_rule_set(NextFlat, Zone, Rules, EndingRule, NewFlats);
true ->
% zone change
case ezic_date:equal(ZoneDate, UTCEndingRuleDate) of
false ->
% zone only
{IEF, INF}= finish_and_start_flat(FlatStart, Zone, DSTOffset),
{[IEF | Flats], INF, CurrentRule};
true ->
% zone AND rule
{IEF, INF}= finish_flatzone_both(FlatStart, Zone, EndingRule, DSTOffset),
{[IEF | Flats], INF, EndingRule}
end
end
end.
finish_and_start_flat(FlatStub=#flatzone{}, NewRule=#rule{save=NewDSTSave}, _EndingRuleDate={{ERDY,_,_},_}, Offset, OldDSTOffset) ->
%% rule is ending, while timezone remains
finish_and_start_flat(FlatStub=#flatzone{offset=Offset, dstoffset=OldDSTOffset}
, NewRule=#rule{save=NewDSTSave}
, _EndingRuleDate={{ERDY,_,_},_}) ->
%% @todo for_rule_all was already called in a loop earlier. use those values instead.
{{WD, WDn}, SD, UD}= ezic_date:for_rule(NewRule, Offset, OldDSTOffset, NewDSTSave, ERDY),
{WDm, SDm, UDm}= ezic_date:m1s({WD, SD, UD}),
@@ -172,9 +194,10 @@ finish_and_start_flat(FlatStub=#flatzone{}, NewRule=#rule{save=NewDSTSave}, _End
?debugVal(EndFlat),
?debugVal(FinalNewFlat),
{EndFlat, FinalNewFlat}.
{EndFlat, FinalNewFlat};
%% timezone is ending, while rule remains the same.
%% returns the finished flatzone for the current zone, and a stub for
%% the next zone with the current DST offset and the UTC start
%% datetime
@@ -186,11 +209,25 @@ finish_and_start_flat(FlatStub=#flatzone{}, Zone=#zone{}, EndingDST) ->
RetNextFlat= #flatzone{dstoffset=EndingDST, utc_from=UD},
?debugVal(EndFlat),
?debugVal(RetNextFlat),
% ?debugVal(RetNextFlat),
{EndFlat, RetNextFlat}.
%% both timezone and rule are ending at the same time
finish_flatzone_both(FlatStub=#flatzone{}, EndingZone=#zone{}, ChangingRule=#rule{save=NewDST}, EndingDST) ->
?debugVal(FlatStub),
EndDatesP1={WD,SD,UD}= ezic_zone:project_end(EndingZone, EndingDST),
{WDm, SDm, UDm}= ezic_date:m1s(EndDatesP1),
EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, EndingDST),
?debugVal(EndFlat),
?debugVal(ChangingRule),
RetNextFlat= #flatzone{dstoffset=NewDST, utc_from=UD},
{EndFlat, RetNextFlat}.
populate_flatzone(
FZ=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
@@ -199,3 +236,14 @@ populate_flatzone(
UTCFromTZ= ezic_date:normalize(UTCFrom, u),
{WT, ST, UTCFRom}= ezic_date:all_times(UTCFromTZ, Offset, DSTOffset),
FZ#flatzone{offset=Offset, tzname=Name, wall_from=WT, std_from=ST}.
maxyear_reached({{Y,_,_},_}) when Y > ?MAXYEAR ->
true;
maxyear_reached({{Y,_,_},_}) when Y =< ?MAXYEAR ->
false;
maxyear_reached(Atom) when Atom=:=maximum; Atom=:=current ->
true; % for some N, taking n > N => maximum > ?MAXYEAR
maxyear_reached(Val) ->
erlang:error(bad_year, Val).