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:
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user