roughed-in the "nested" rule processing (flatten). hurdle on rule years->max.

This commit is contained in:
aj
2010-11-10 13:01:03 -08:00
parent e48e793a36
commit 1520c631ef
4 changed files with 105 additions and 28 deletions

View File

@@ -49,7 +49,7 @@ load(Folder) ->
dev_start() ->
ezic:load(filename:join("priv","tzdata")),
% ezic:load(filename:join("priv","tzdata")),
ezic_flatten:flatten().

View File

@@ -10,6 +10,7 @@
% rule-specific
, for_rule/2
, for_rule_utc/4
, for_rule_all/4
% converters
@@ -58,10 +59,14 @@ for_rule(#rule{in=M, on=#tzon{day=Day, filter=Filter}, at=At}, Y) ->
{first_day_limited(Day, Filter, Y,M), At}.
for_rule_all(Rule, Offset, DSTOffset, Year) ->
DT= for_rule(Rule, Year),
all_times(DT, Offset, DSTOffset).
% returns UTC datetime for rule, offset, and year
for_rule_utc(Rule, Offset, DSTOffset, Year) ->
DT= for_rule(Rule, Year),
{_,_,UTCDatetime} = all_times(DT, Offset, DSTOffset),
{_,_,UTCDatetime} = for_rule_all(Rule, Offset, DSTOffset, Year),
UTCDatetime.
@@ -162,6 +167,14 @@ all_times({Date, #tztime{time=WallTime, flag=Flag}}, Offset, DSTOffset)
compare(_, current) ->
true;
compare(current, current) ->
true;
compare(current, _) ->
false;
% returns true if DT1 =< DT2. False otherwise. can be used with lists:sort/2
% both times are assumed to be in the same zone/DST context
compare(DT1={{Y1,M1,D1},{HH1,MM1,SS1}}, DT2={{Y2,M2,D2},{HH2,MM2,SS2}})
@@ -265,12 +278,14 @@ add_days_in_month(Days, Date={Y,M,D}) ->
m1s(D= {{Y,M,D},{HH,MM,SS}})
m1s(Date= {{Y,M,D},{HH,MM,SS}})
when is_integer(Y), is_integer(M), is_integer(D)
, is_integer(HH), is_integer(MM), is_integer(SS) ->
calendar:gregorian_seconds_to_datetime(calendar:datetime_to_gregorian_seconds(D) - 1).
calendar:gregorian_seconds_to_datetime(calendar:datetime_to_gregorian_seconds(Date) - 1);
m1s({WD, SD, UD}) ->
{m1s(WD), m1s(SD), m1s(UD)}.
% @todo type checking
m1s(W,S,U) when is_tuple(W), is_tuple(S), is_tuple(U) ->

View File

@@ -4,7 +4,7 @@
-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(ENDFLAT(F,W,S,U,D), F#flatzone{wall_to=W, std_to=S, utc_to=U, dstoffset=D}).
-define(MINFLAT, ?FLAT(minimum,minimum,minimum)).
@@ -45,6 +45,7 @@ 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
flatten_zone_set(Zones) ->
flatten_zone_set(?MINFLAT, Zones, []).
@@ -72,8 +73,52 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
%% we gather all rules that _may_ apply (same year)
Rules= ezic_db:rules(RuleName),
%% and add normalized (inaccurate) dates for sorting purposes
%% tack the date onto the zone, so we can see if it ends before a rule does
ZoneWithDate= {ezic_zone:project_end_utc(Zone, DSTOffset), Zone},
?debugVal(FromTime),
?debugVal(ZoneWithDate),
?debugVal(Rules),
RuleFlats= flatten_rule_set(FromTime, ZoneWithDate, Rules, []),
?debugVal(RuleFlats),
%% %% and add normalized (possibly inaccurate) dates for sorting purposes
%% %% note this may be empty
%% RulesWithDates= lists:foldl(
%% fun(R, Acc)->
%% case ezic_rule:project_next(R, Offset, DSTOffset, UTCFrom) of
%% none -> Acc;
%% D -> [{D,R} | Acc]
%% end
%% end
%% ,[], Rules),
%% [NextEventWithDate | RestWithDates]= lists:sort(RulesAndZoneWithDates),
%% {_,NextEvent}= NextEventWithDate,
%% {EndFlat, NextFlat}= finish_and_start_flat(FromTime, NextEvent, Offset, DSTOffset),
%% NewFlats= [EndFlat | Flats],
%% RestZones= [Z || {_,Z} <- RestWithDates],
%% flatten_zone_set(NextFlat, RestZones, NewFlats).
not_done.
flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offset=Offset}
, ZoneWithDate, Rules, Flats) ->
%% and 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)
RulesWithDates= lists:foldl(
fun(R, Acc)->
case ezic_rule:project_next(R, Offset, DSTOffset, UTCFrom) of
@@ -82,33 +127,48 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
end
end
,[], Rules),
%% tack on the zone w/ date, in case it ends first
ZoneWithDate= {ezic_zone:project_end_utc(Zone, DSTOffset), Zone},
[NextEventWithDate | RestWithDates]= lists:sort(RulesAndZoneWithDates),
?debugVal(NextEventWithDate),
?debugVal(RestWithDates),
{_,NextEvent}= NextEventWithDate,
{EndFlat, NextFlat}= finish_and_start_flat(FromTime, NextEvent, Offset, DSTOffset),
NewFlats= [EndFlat | Flats],
RestZones= [Z || {_,Z} <- RestWithDates],
?debugVal(RulesWithDates),
flatten_zone_set(NextFlat, RestZones, NewFlats).
[{EndingRuleDate, EndingRule} | _]= lists:sort(RulesWithDates),
{ZoneDate, Zone}= ZoneWithDate,
?debugVal(EndingRuleDate),
?debugVal(ZoneDate),
case ezic_date:compare(EndingRuleDate, ZoneDate) of
true ->
%% same zone, new rule
{EndFlat, NextFlat}= finish_and_start_flat(FlatStart, EndingRule, EndingRuleDate, Offset, DSTOffset),
NewFlats= [EndFlat | Flats],
flatten_rule_set(NextFlat, ZoneWithDate, Rules, NewFlats);
false ->
%% new zone is handled in calling function: flatten_zone_set
Flats
end.
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),
finish_and_start_flat(Flat=#flatzone{}, Zone=#zone{}, _Offset, DSTOffset) ->
NextStart= {WT, ST, UT}= ezic_zone:project_end(Zone, DSTOffset),
{MWT, MST, MUT}= ezic_date:m1s(NextStart),
EndFlat= ?ENDFLAT(Flat, MWT, MST, MUT),
NextFlat= ?FLAT(WT, ST, UT),
{EndFlat, NextFlat};
EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset),
NewFlat1= ?FLAT(WD, SD, UD),
NewFlat2= NewFlat1#flatzone{offset=Offset},
FinalNewFlat= NewFlat2,
{EndFlat, FinalNewFlat}.
%% finish_and_start_flat(Flat=#flatzone{}, Zone=#zone{}, _Offset, DSTOffset) ->
%% NextStart= {WT, ST, UT}= ezic_zone:project_end(Zone, DSTOffset),
%% {MWT, MST, MUT}= ezic_date:m1s(NextStart),
%% EndFlat= ?ENDFLAT(Flat, MWT, MST, MUT),
%% NextFlat= ?FLAT(WT, ST, UT),
%% {EndFlat, NextFlat};
finish_and_start_flat(Flat=#flatzone{utc_from=UTCFrom}, Rule=#rule{}, Offset, DSTOffset) ->
not_done.

View File

@@ -26,6 +26,8 @@ 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) ->
ezic_date:for_rule_utc(Rule, Offset, DSTOffset, RFrom);
project_next(Rule=#rule{from=RFrom, to=RTo}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) ->
AllYears= years(Rule),
GoodYears= years_after(AY, AllYears),
@@ -43,7 +45,7 @@ years(Rule=#rule{from=From, to=only}) ->
years(Rule=#rule{from=From, to=To}) ->
try lists:seq(From, To)
catch error:function_clause ->
erlang:error(bad_year_range)
erlang:error(bad_year_range, [From, To])
end.
% returns the years from the list that are greater than OR equal to Y