roughed-in the "nested" rule processing (flatten). hurdle on rule years->max.
This commit is contained in:
@@ -49,7 +49,7 @@ load(Folder) ->
|
||||
|
||||
|
||||
dev_start() ->
|
||||
ezic:load(filename:join("priv","tzdata")),
|
||||
% ezic:load(filename:join("priv","tzdata")),
|
||||
ezic_flatten:flatten().
|
||||
|
||||
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user