diff --git a/src/ezic_date.erl b/src/ezic_date.erl index 43c3341..d269561 100644 --- a/src/ezic_date.erl +++ b/src/ezic_date.erl @@ -37,9 +37,10 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% converts any sane date part into a complete datetime -% USED EXCLUSIVELY FOR COMPARISONS. The resulting "datetime" values may not -% represent true dates or times. +%% converts any sane date part into a complete datetime +%% USED EXCLUSIVELY FOR COMPARISONS. The resulting "datetime" values may not +%% represent true dates or times. +%% @todo ensure atom is valid normalize(A) when is_atom(A) -> A; normalize(Y) when is_integer(Y) -> @@ -179,6 +180,14 @@ compare(current, _) -> false; +compare(_, X) when X=:=max; X=:=maximum -> + true; +compare(X, X) when X=:=max; X=:=maximum -> + true; +compare(X, _) when X=:=max; X=:=maximum -> + 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}}) diff --git a/src/ezic_flatten.erl b/src/ezic_flatten.erl index 6d96c20..4e4b9d0 100644 --- a/src/ezic_flatten.erl +++ b/src/ezic_flatten.erl @@ -60,18 +60,19 @@ flatten_zone_set(Zones) -> % % assumes a new zone every time it is called flatten_zone_set(_, [], Flats) -> - 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) -> - [Zone | _RestZones] = ezic_zone:next(Zones, UTCFrom, DSTOffset), + [Zone | RestZones] = ezic_zone:next(Zones, UTCFrom, DSTOffset), #zone{rule=RuleName, until=_UntilTime, gmtoff=Offset}=Zone, %% we have a flatzone with start times; must populate the base gmt offset - FromTime= FromTimeStub#flatzone{offset=Offset}, + 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 DST offset + %% if this is a recursion, DST offset is the previous zone's last DST offset %% we gather all rules that _may_ apply (same year) @@ -79,38 +80,27 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} %% tack the date onto the zone, so we can see if the zone ends before a rule does - ZoneWithDate= {ezic_zone:project_end_utc(Zone, DSTOffset), Zone}, + TempZoneEndUTC= ezic_zone:project_end_utc(Zone, DSTOffset), + ZoneWithDate= {TempZoneEndUTC, Zone}, ?debugVal(FromTime), ?debugVal(ZoneWithDate), ?debugVal(Rules), - RuleFlats= flatten_rule_set(FromTime, ZoneWithDate, Rules, []), + {RuleFlats, LastFlat, EndingDST}= 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], + ?debugVal(LastFlat), + ?debugVal(EndingDST), - %% RestZones= [Z || {_,Z} <- RestWithDates], + %% rules have been exhausted, and zone is ending. + {FinalFlat, NextFlat}= finish_and_start_flat(LastFlat, Zone, EndingDST), + ?debugVal(FinalFlat), + FinalFlats= lists:merge([[FinalFlat], RuleFlats, Flats]), + ?debugVal(FinalFlats), - %% flatten_zone_set(NextFlat, RestZones, NewFlats). - not_done. + flatten_zone_set(NextFlat, RestZones, FinalFlats). @@ -135,7 +125,12 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs ?debugVal(RulesWithDates), - [{EndingRuleDate, EndingRule} | _]= lists:sort(RulesWithDates), + {EndingRuleDate, EndingRule}= + case length(RulesWithDates) > 0 of + false -> {maximum, none}; + true -> hd(lists:sort(RulesWithDates)) + end, + {ZoneDate, _Zone}= ZoneWithDate, ?debugVal(EndingRuleDate), @@ -149,32 +144,40 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs flatten_rule_set(NextFlat, ZoneWithDate, Rules, NewFlats); false -> %% new zone is handled in the caller: flatten_zone_set - Flats + {Flats, FlatStart, DSTOffset} end. -finish_and_start_flat(FlatStub=#flatzone{utc_from=_UTCFrom}, EndingRule=#rule{}, _EndingRuleDate={{ERDY,_,_},_}, Offset, DSTOffset) -> +finish_and_start_flat(FlatStub=#flatzone{utc_from=_UTCFrom}, NewRule=#rule{save=NewDSTSave}, _EndingRuleDate={{ERDY,_,_},_}, Offset, DSTOffset) -> %% @todo for_rule_all was already called in a loop earlier. use those values instead. - NewFlatStartDates={WD, SD, UD}= ezic_date:for_rule_all(EndingRule, Offset, DSTOffset, ERDY), + NewFlatStartDates={WD, SD, UD}= ezic_date:for_rule_all(NewRule, Offset, DSTOffset, ERDY), {WDm, SDm, UDm}= ezic_date:m1s(NewFlatStartDates), EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset), NewFlat1= ?FLAT(WD, SD, UD), - NewFlat2= NewFlat1#flatzone{offset=Offset}, + NewFlat2= NewFlat1#flatzone{offset=Offset, dstoffset=NewDSTSave, tzname=EndFlat#flatzone.tzname}, FinalNewFlat= NewFlat2, + + ?debugVal(EndFlat), + ?debugVal(FinalNewFlat), + {EndFlat, FinalNewFlat}. +finish_and_start_flat(FlatStub=#flatzone{}, Zone=#zone{}, EndingDST) -> + EndDatesP1={WD,SD,UD}= ezic_zone:project_end(Zone, EndingDST), + {WDm, SDm, UDm}= ezic_date:m1s(EndDatesP1), + EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, EndingDST), + + NextFlat1= ?FLAT(WD,SD,UD), + NextFlat2= NextFlat1#flatzone{dstoffset=EndingDST}, + RetNextFlat= NextFlat2, -%% 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}; - + ?debugVal(EndFlat), + ?debugVal(RetNextFlat), + {EndFlat, RetNextFlat}. diff --git a/src/ezic_rule.erl b/src/ezic_rule.erl index f8ff39f..c5d768a 100644 --- a/src/ezic_rule.erl +++ b/src/ezic_rule.erl @@ -42,6 +42,8 @@ project_next(Rule=#rule{}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) -> % returns the sorted list of years a rule existed for. years(#rule{from=From, to=only}) -> [From]; +years(#rule{from=From, to=X}) when X=:=max; X=:=maximum-> + [From,9999]; % let's hope this isn't used past year 9999 years(#rule{from=From, to=To}) -> try lists:seq(From, To) catch error:function_clause -> diff --git a/test/ezic_date_tests.erl b/test/ezic_date_tests.erl index e0db655..4972851 100644 --- a/test/ezic_date_tests.erl +++ b/test/ezic_date_tests.erl @@ -58,3 +58,10 @@ add_seconds_test_() -> ]. + + +compare_test_() -> + [ + ?_assert(ezic_date:compare({2011,12,12}, current)) + , ?_assertNot(ezic_date:compare(current, {2099,12,12})) % is this the right behavior? + ].