there is a subtle bug in wall-time calculations. Asia/Irkutsk times are incorrect.
This commit is contained in:
@@ -37,9 +37,10 @@
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
|
||||||
% converts any sane date part into a complete datetime
|
%% converts any sane date part into a complete datetime
|
||||||
% USED EXCLUSIVELY FOR COMPARISONS. The resulting "datetime" values may not
|
%% USED EXCLUSIVELY FOR COMPARISONS. The resulting "datetime" values may not
|
||||||
% represent true dates or times.
|
%% represent true dates or times.
|
||||||
|
%% @todo ensure atom is valid
|
||||||
normalize(A) when is_atom(A) ->
|
normalize(A) when is_atom(A) ->
|
||||||
A;
|
A;
|
||||||
normalize(Y) when is_integer(Y) ->
|
normalize(Y) when is_integer(Y) ->
|
||||||
@@ -179,6 +180,14 @@ compare(current, _) ->
|
|||||||
false;
|
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
|
% 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}})
|
||||||
|
|||||||
@@ -60,18 +60,19 @@ flatten_zone_set(Zones) ->
|
|||||||
%
|
%
|
||||||
% assumes a new zone every time it is called
|
% assumes a new zone every time it is called
|
||||||
flatten_zone_set(_, [], Flats) ->
|
flatten_zone_set(_, [], Flats) ->
|
||||||
Flats;
|
erlang:error(debug_quit),
|
||||||
|
Flats;
|
||||||
flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
|
flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset}
|
||||||
, Zones %[Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | _RestZones],
|
, 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,
|
#zone{rule=RuleName, until=_UntilTime, gmtoff=Offset}=Zone,
|
||||||
|
|
||||||
%% we have a flatzone with start times; must populate the base gmt offset
|
%% 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 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)
|
%% 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
|
%% 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(FromTime),
|
||||||
?debugVal(ZoneWithDate),
|
?debugVal(ZoneWithDate),
|
||||||
?debugVal(Rules),
|
?debugVal(Rules),
|
||||||
|
|
||||||
RuleFlats= flatten_rule_set(FromTime, ZoneWithDate, Rules, []),
|
{RuleFlats, LastFlat, EndingDST}= flatten_rule_set(FromTime, ZoneWithDate, Rules, []),
|
||||||
?debugVal(RuleFlats),
|
?debugVal(RuleFlats),
|
||||||
|
?debugVal(LastFlat),
|
||||||
%% %% and add normalized (possibly inaccurate) dates for sorting purposes
|
?debugVal(EndingDST),
|
||||||
%% %% 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],
|
%% 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),
|
?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,
|
{ZoneDate, _Zone}= ZoneWithDate,
|
||||||
|
|
||||||
?debugVal(EndingRuleDate),
|
?debugVal(EndingRuleDate),
|
||||||
@@ -149,32 +144,40 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs
|
|||||||
flatten_rule_set(NextFlat, ZoneWithDate, Rules, NewFlats);
|
flatten_rule_set(NextFlat, ZoneWithDate, Rules, NewFlats);
|
||||||
false ->
|
false ->
|
||||||
%% new zone is handled in the caller: flatten_zone_set
|
%% new zone is handled in the caller: flatten_zone_set
|
||||||
Flats
|
{Flats, FlatStart, DSTOffset}
|
||||||
end.
|
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.
|
%% @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),
|
{WDm, SDm, UDm}= ezic_date:m1s(NewFlatStartDates),
|
||||||
|
|
||||||
EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset),
|
EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, DSTOffset),
|
||||||
NewFlat1= ?FLAT(WD, SD, UD),
|
NewFlat1= ?FLAT(WD, SD, UD),
|
||||||
NewFlat2= NewFlat1#flatzone{offset=Offset},
|
NewFlat2= NewFlat1#flatzone{offset=Offset, dstoffset=NewDSTSave, tzname=EndFlat#flatzone.tzname},
|
||||||
|
|
||||||
FinalNewFlat= NewFlat2,
|
FinalNewFlat= NewFlat2,
|
||||||
|
|
||||||
|
?debugVal(EndFlat),
|
||||||
|
?debugVal(FinalNewFlat),
|
||||||
|
|
||||||
{EndFlat, 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) ->
|
?debugVal(EndFlat),
|
||||||
%% NextStart= {WT, ST, UT}= ezic_zone:project_end(Zone, DSTOffset),
|
?debugVal(RetNextFlat),
|
||||||
%% {MWT, MST, MUT}= ezic_date:m1s(NextStart),
|
|
||||||
%% EndFlat= ?ENDFLAT(Flat, MWT, MST, MUT),
|
|
||||||
%% NextFlat= ?FLAT(WT, ST, UT),
|
|
||||||
%% {EndFlat, NextFlat};
|
|
||||||
|
|
||||||
|
|
||||||
|
{EndFlat, RetNextFlat}.
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ project_next(Rule=#rule{}, Offset, DSTOff, UTCAfter={{AY,_,_},_}) ->
|
|||||||
% returns the sorted list of years a rule existed for.
|
% returns the sorted list of years a rule existed for.
|
||||||
years(#rule{from=From, to=only}) ->
|
years(#rule{from=From, to=only}) ->
|
||||||
[From];
|
[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}) ->
|
years(#rule{from=From, to=To}) ->
|
||||||
try lists:seq(From, To)
|
try lists:seq(From, To)
|
||||||
catch error:function_clause ->
|
catch error:function_clause ->
|
||||||
|
|||||||
@@ -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?
|
||||||
|
].
|
||||||
|
|||||||
Reference in New Issue
Block a user