143 lines
3.6 KiB
Erlang
143 lines
3.6 KiB
Erlang
-module(ezic_flatten).
|
|
-include("include/ezic.hrl").
|
|
|
|
-define(FLAT(W,S,U), #flatzone{wall_from=W, std_from=S, utc_from=U}).
|
|
-define(ENDFLAT(F,W,S,U), F#flatzone{wall_to=W, std_to=S, utc_to=U}).
|
|
-define(MINFLAT, ?FLAT(minimum,minimum,minimum)).
|
|
|
|
|
|
|
|
|
|
-export([flatten/0]).
|
|
|
|
|
|
|
|
|
|
flatten() ->
|
|
AllZones= ezic_db:get_all(zone),
|
|
flatten_zones(AllZones),
|
|
not_done.
|
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% PRIVATE
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
% 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, []).
|
|
|
|
|
|
|
|
make_next_flat(#flatzone{wall_to=W, std_to=S, utc_to=U, offset=O}) ->
|
|
make_next_flat({W,S,U}, O);
|
|
make_next_flat(EndTimes) ->
|
|
make_next_flat(EndTimes, undefined).
|
|
|
|
|
|
make_next_flat({W,S,U}, O) ->
|
|
FW= ezic_date:add_seconds(W,1),
|
|
FS= ezic_date:add_seconds(S,1),
|
|
FU= ezic_date:add_seconds(U,1),
|
|
Flat= ?FLAT(FW, FS, FU),
|
|
Flat#flatzone{offset=O}.
|
|
|
|
|
|
|
|
end_flat(Flat=#flatzone{}, {W,S,U}) ->
|
|
?ENDFLAT(Flat,W,S,U).
|
|
|
|
|
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
% INTERNAL
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
|
|
|
|
|
flatten_zones([], Flats) ->
|
|
Flats;
|
|
flatten_zones([Z1|_]= AllZones, Flats) ->
|
|
{CurrentZones, RestZones}= ezic_zone:split_by_name(Z1, AllZones),
|
|
|
|
SortedZones= lists:sort(fun ezic_zone:sort_ascending/2, CurrentZones),
|
|
NewFlats= lists:merge(Flats, flatten_zone_set(SortedZones)),
|
|
|
|
flatten_zones(RestZones, NewFlats).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% takes zones one-by-one, gathering relevant rules and creating flat periods of the same gmt offset (#flatzone)
|
|
flatten_zone_set(Zones) ->
|
|
flatten_zone_set(?MINFLAT, Zones, []).
|
|
|
|
|
|
% flatten_zone_set(FromTime, Zones, Flats) -> [#flatzone{}]
|
|
% FromTime= #flatzone{*_from =/= undefined}
|
|
% Zones= [#zone{}]
|
|
% Flats= [#flatzone{}]
|
|
%
|
|
% assumes a new zone every time it is called
|
|
flatten_zone_set(_, [], Flats) ->
|
|
Flats;
|
|
flatten_zone_set(FromTimeStub, [Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | _RestZones] = _Zones, _Flats) ->
|
|
|
|
%% we have a flatzone with start times, we populate the base offset
|
|
FromTime= FromTimeStub#flatzone{offset=Offset},
|
|
|
|
|
|
%% we gather all rules that _may_ apply (same year)
|
|
Rules= ezic_db:rules(RuleName),
|
|
RelevantRules= ezic_rule:filter(FromTime, UntilTime, Rules),
|
|
SortedRules= lists:sort(fun ezic_rule:sort_ascending/2, RelevantRules),
|
|
|
|
|
|
%% Then we plot points, bouncing from rule to rule, ending when the zone ends
|
|
{ok, _NextFromTimeStub, _NewFlats}= flatten_zone(FromTime, Z1, SortedRules),
|
|
|
|
|
|
not_done.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
% flattens a single zone into multiple flatzones.
|
|
% FromTime has its *_from fields and offset field populated. (it should anyway)
|
|
% @todo invalid input checking
|
|
% Rules are sorted
|
|
flatten_zone(BaseFlat, Zone, Rules) ->
|
|
flatten_zone(BaseFlat, Zone, Rules, []).
|
|
|
|
flatten_zone(BaseFlat, Zone=#zone{until=Until, gmtoff=Offset}, Rules, Flats) ->
|
|
case ezic_rule:next_event(BaseFlat, Until, Offset, Rules) of
|
|
% is this enough information to compare all times corectly?
|
|
{end_until, EndTimes} ->
|
|
NewFlats= [end_flat(BaseFlat, EndTimes) | Flats],
|
|
NewFrom= make_next_flat(EndTimes),
|
|
{ok, NewFrom, NewFlats}; % base case
|
|
|
|
{end_rule, EndTimes, NextOffset} ->
|
|
NewFlats= [end_flat(BaseFlat, EndTimes) | Flats],
|
|
NewFrom= make_next_flat(EndTimes, NextOffset),
|
|
flatten_zone(NewFrom, Zone, Rules, NewFlats) % tail recursion
|
|
|
|
end.
|
|
|
|
|
|
|
|
|