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() ->
|
dev_start() ->
|
||||||
ezic:load(filename:join("priv","tzdata")),
|
% ezic:load(filename:join("priv","tzdata")),
|
||||||
ezic_flatten:flatten().
|
ezic_flatten:flatten().
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
% rule-specific
|
% rule-specific
|
||||||
, for_rule/2
|
, for_rule/2
|
||||||
, for_rule_utc/4
|
, for_rule_utc/4
|
||||||
|
, for_rule_all/4
|
||||||
|
|
||||||
|
|
||||||
% converters
|
% 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}.
|
{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
|
% returns UTC datetime for rule, offset, and year
|
||||||
for_rule_utc(Rule, Offset, DSTOffset, Year) ->
|
for_rule_utc(Rule, Offset, DSTOffset, Year) ->
|
||||||
DT= for_rule(Rule, Year),
|
{_,_,UTCDatetime} = for_rule_all(Rule, Offset, DSTOffset, Year),
|
||||||
{_,_,UTCDatetime} = all_times(DT, Offset, DSTOffset),
|
|
||||||
UTCDatetime.
|
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
|
% 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
|
% 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}})
|
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)
|
when is_integer(Y), is_integer(M), is_integer(D)
|
||||||
, is_integer(HH), is_integer(MM), is_integer(SS) ->
|
, 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
|
% @todo type checking
|
||||||
m1s(W,S,U) when is_tuple(W), is_tuple(S), is_tuple(U) ->
|
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(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)).
|
-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)
|
% 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(Zones) ->
|
||||||
flatten_zone_set(?MINFLAT, 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)
|
%% we gather all rules that _may_ apply (same year)
|
||||||
Rules= ezic_db:rules(RuleName),
|
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
|
%% 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(
|
RulesWithDates= lists:foldl(
|
||||||
fun(R, Acc)->
|
fun(R, Acc)->
|
||||||
case ezic_rule:project_next(R, Offset, DSTOffset, UTCFrom) of
|
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
|
||||||
end
|
end
|
||||||
,[], Rules),
|
,[], 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) ->
|
EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset),
|
||||||
NextStart= {WT, ST, UT}= ezic_zone:project_end(Zone, DSTOffset),
|
NewFlat1= ?FLAT(WD, SD, UD),
|
||||||
{MWT, MST, MUT}= ezic_date:m1s(NextStart),
|
NewFlat2= NewFlat1#flatzone{offset=Offset},
|
||||||
EndFlat= ?ENDFLAT(Flat, MWT, MST, MUT),
|
|
||||||
NextFlat= ?FLAT(WT, ST, UT),
|
FinalNewFlat= NewFlat2,
|
||||||
{EndFlat, NextFlat};
|
{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,
|
% 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.
|
% 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,_,_},_}) ->
|
project_next(Rule=#rule{from=RFrom, to=RTo}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) ->
|
||||||
AllYears= years(Rule),
|
AllYears= years(Rule),
|
||||||
GoodYears= years_after(AY, AllYears),
|
GoodYears= years_after(AY, AllYears),
|
||||||
@@ -43,7 +45,7 @@ years(Rule=#rule{from=From, to=only}) ->
|
|||||||
years(Rule=#rule{from=From, to=To}) ->
|
years(Rule=#rule{from=From, to=To}) ->
|
||||||
try lists:seq(From, To)
|
try lists:seq(From, To)
|
||||||
catch error:function_clause ->
|
catch error:function_clause ->
|
||||||
erlang:error(bad_year_range)
|
erlang:error(bad_year_range, [From, To])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
% returns the years from the list that are greater than OR equal to Y
|
% returns the years from the list that are greater than OR equal to Y
|
||||||
|
|||||||
Reference in New Issue
Block a user