diff --git a/src/ezic_flatten.erl b/src/ezic_flatten.erl index 7b5a1ad..dd273b9 100644 --- a/src/ezic_flatten.erl +++ b/src/ezic_flatten.erl @@ -1,6 +1,5 @@ -module(ezic_flatten). -include("include/ezic.hrl"). --include_lib("eunit/include/eunit.hrl"). -define(FLAT(W,S,U), #flatzone{wall_from=W, std_from=S, utc_from=U}). @@ -41,12 +40,8 @@ ms(Date, Name) -> R= ['$_'], {D, #tztime{time=T, flag=F}}=Date, - - %SDate= {D,T}, - DComp= {{{D},{T}}}, - + DComp= {{ {D},{T} }}, G= ms_guards(F, DComp), - MS= [{M,G,R}], @@ -77,12 +72,9 @@ flatten_all_zones(AllZones, AllRules) -> flatten_all_zones([], _AllRules, FlatZones) -> FlatZones; flatten_all_zones([Z1|_]= AllZones, AllRules, FlatZones) -> - %% io:format("Flattening zones: ~s~n", [Z1#zone.name]), + io:format("Flattening zones: ~s~n", [Z1#zone.name]), {CurrentZones, RestZones}= ezic_zone:split_by_name(Z1, AllZones), - Flats= flatten_zone_set(CurrentZones, AllRules), - %ezic_db:insert_all(Flats), - flatten_all_zones(RestZones, AllRules, Flats ++ FlatZones). @@ -128,7 +120,7 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} [Zone | RestZones] = ezic_zone:next(Zones, UTCFrom, DSTOffset), #zone{rule=RuleName}= Zone, - ?debugVal(Zone), + %% ?debugVal(Zone), %% we have a flatzone with start times; %% must populate the base gmt offset and name for the current zone @@ -137,17 +129,16 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} %% if this is the first run, DST offset is {0,0,0} if this is a %% recursion, DST offset is the previous zone's last DST offset - %% @todo see if dst rules carry over zone changes IRL. I'll assume - %% they don't. - - %% we gather all rules that _may_ apply - %Rules= ezic_db:rules(RuleName), - Rules= [R || R <- AllRules, R#rule.name =:= RuleName ], - - ?debugVal(FromTime), - ?debugVal(Rules), + %% @todo see if dst rules carry over zone changes IRL. This + %% assumes they don't. + %% gather all rules that _may_ apply + + Rules= [R || R <- AllRules, R#rule.name =:= RuleName ], + + %% ?debugVal(FromTime), + %% ?debugVal(Rules), %% apply all rules in order, creating flatzones, until this zone %% ends, then regain control. rules have been exhausted, and zone @@ -156,21 +147,21 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} {RuleFlats, NextFlat, EndingRule}= flatten_rule_set(FromTime, Zone, Rules, CurrentRule, []), FinalFlats= lists:append([RuleFlats, Flats]), - ?debugVal(FinalFlats), - ?debugVal(NextFlat), + %% ?debugVal(FinalFlats), + %% ?debugVal(NextFlat), %% return flats if we've exceeded our years, or recurse if we can keep going NFUTCFrom = NextFlat#flatzone.utc_from, try maxyear_reached(NFUTCFrom) of true -> - ?debugMsg("maxyear reached from flatten_zone_set"), + %% ?debugMsg("maxyear reached from flatten_zone_set"), FinalFlats; false -> flatten_zone_set(NextFlat, RestZones, AllRules, FinalFlats, EndingRule) catch exit:Reason -> - ?debugMsg("bad year for nextflat:"), - ?debugVal(NextFlat), + %% ?debugMsg("bad year for nextflat:"), + %% ?debugVal(NextFlat), erlang:error(Reason) end. @@ -181,10 +172,9 @@ flatten_zone_set(FromTimeStub=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offset=Offset} , Zone, Rules, CurrentRule, Flats) -> - - ?debugVal(Offset), - ?debugVal(DSTOffset), - ?debugVal(UTCFrom), + %% ?debugVal(Offset), + %% ?debugVal(DSTOffset), + %% ?debugVal(UTCFrom), ValidRules= lists:delete(CurrentRule, Rules), RulesWithDates= lists:foldl( @@ -206,15 +196,15 @@ flatten_rule_set(FlatStart=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset, offs ZoneDate= ezic_zone:project_end_utc(Zone, DSTOffset), - ?debugVal(UTCEndingRuleDate), - ?debugVal(EndingRuleYear), - ?debugVal(EndingRule), - ?debugVal(ZoneDate), + %% ?debugVal(UTCEndingRuleDate), + %% ?debugVal(EndingRuleYear), + %% ?debugVal(EndingRule), + %% ?debugVal(ZoneDate), try maxyear_reached(UTCEndingRuleDate) andalso maxyear_reached(ZoneDate) of true -> - ?debugMsg("maxyear reached from flatten_rule_set"), + %% ?debugMsg("maxyear reached from flatten_rule_set"), {EndFlat, NextFlat}= finish_and_start_flat(max_year, FlatStart, DSTOffset), NewFlats= [EndFlat | Flats], {NewFlats, NextFlat, none}; @@ -263,8 +253,8 @@ finish_and_start_flat(FlatStub=#flatzone{offset=Offset, dstoffset=OldDSTOffset} FinalNewFlat= NewFlat2, - ?debugVal(EndFlat), - ?debugVal(FinalNewFlat), + %% ?debugVal(EndFlat), + %% ?debugVal(FinalNewFlat), {EndFlat, FinalNewFlat}; @@ -280,7 +270,7 @@ finish_and_start_flat(FlatStub=#flatzone{}, Zone=#zone{}, EndingDST) -> RetNextFlat= #flatzone{dstoffset=EndingDST, utc_from=UD}, - ?debugVal(EndFlat), + %% ?debugVal(EndFlat), % ?debugVal(RetNextFlat), {EndFlat, RetNextFlat}; @@ -295,23 +285,21 @@ finish_and_start_flat(max_year, FlatStub, DSTOffset) -> %% both timezone and rule are ending at the same time finish_flatzone_both(FlatStub=#flatzone{}, EndingZone=#zone{}, ChangingRule=#rule{save=NewDST}, EndingDST) -> - ?debugVal(FlatStub), + %% ?debugVal(FlatStub), EndDatesP1={_,_,UD}= ezic_zone:project_end(EndingZone, EndingDST), {WDm, SDm, UDm}= ezic_date:m1s(EndDatesP1), EndFlat= ?ENDFLAT(FlatStub, WDm, SDm, UDm, EndingDST), - ?debugVal(EndFlat), - ?debugVal(ChangingRule), + %% ?debugVal(EndFlat), + %% ?debugVal(ChangingRule), RetNextFlat= #flatzone{dstoffset=NewDST, utc_from=UD}, {EndFlat, RetNextFlat}. -populate_flatzone( - FZ=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} - , #zone{name=Name, gmtoff=Offset}) -> - +populate_flatzone(FZ=#flatzone{utc_from=UTCFrom, dstoffset=DSTOffset} + , #zone{name=Name, gmtoff=Offset}) -> UTCFromTZ= ezic_date:normalize(UTCFrom, u), {WT, ST, _}= ezic_date:all_times(UTCFromTZ, Offset, DSTOffset), FZ#flatzone{offset=Offset, tzname=Name, wall_from=WT, std_from=ST}. @@ -332,7 +320,7 @@ maxyear_reached(Val) -> ms_guards(X, D) when X=:=u;X=:=g;X=:=z -> - ms_guards2(D, '$6', '$5'); + ms_guards2(D, '$5', '$6'); ms_guards(s, D) -> ms_guards2(D, '$3', '$4'); ms_guards(X, D) when X=:=w;X=:=undefined -> @@ -342,5 +330,5 @@ ms_guards(X, D) when X=:=w;X=:=undefined -> ms_guards2(D, From, To) -> [ {'=<', From, D} - , {'=<', D, To} + , {'or', {'=<', D, To}, {'=:=', current, To}} ]. diff --git a/src/ezic_zone.erl b/src/ezic_zone.erl index b2a93ce..6394d61 100644 --- a/src/ezic_zone.erl +++ b/src/ezic_zone.erl @@ -30,13 +30,6 @@ parse([Name,GmtOffS,Rule,FormatS | UntilTokens]) -> - - - - - - - current(TzName) -> current_as_of_utc(erlang:universaltime(), TzName). @@ -101,10 +94,10 @@ project_end_utc(Zone=#zone{}, DSTOffset) -> %% though it's very unlikely (and does not exist in the current tz database files). %% this method covers that event, anyhow. see unit tests for examples. next(ZoneList, UTCFrom, DSTOff) -> - ?debugMsg("next:"), - ?debugVal(ZoneList), - ?debugVal(UTCFrom), - + %% ?debugMsg("next:"), + %% ?debugVal(ZoneList), + %% ?debugVal(UTCFrom), + DatedList= lists:map( fun(Z=#zone{until=Until, gmtoff=Offset})-> NUntil= ezic_date:normalize(Until), @@ -113,7 +106,5 @@ next(ZoneList, UTCFrom, DSTOff) -> end , ZoneList), FilteredList= lists:filter(fun({IDt,_})-> ezic_date:compare(UTCFrom, IDt) end, DatedList), - -%x ?debugVal(FilteredList), SortedList= lists:sort(fun({X,_},{Y,_})->ezic_date:compare(X,Y)end, FilteredList), [Z || {_,Z}<- SortedList]. diff --git a/test/ezic_tests.erl b/test/ezic_tests.erl index 14c35b8..62b6a46 100644 --- a/test/ezic_tests.erl +++ b/test/ezic_tests.erl @@ -2,12 +2,44 @@ -include("include/ezic.hrl"). -include_lib("eunit/include/eunit.hrl"). +smoke_test_() -> + [ + %% utc_to_local - normal tests + ?_assertEqual({{2012,12,17},{13,20,0}}, ezic:utc_to_local({{2012,12,17},{4,20,0}}, "Asia/Tokyo")) + , ?_assertEqual({{2012,12,16},{23,20,0}}, ezic:utc_to_local({{2012,12,17},{4,20,0}}, "America/Jamaica")) + , ?_assertEqual({{2012,12,16},{20,20,0}}, ezic:utc_to_local({{2012,12,17},{4,20,0}}, "America/Los_Angeles")) + + %% local_to_utc - normal tests + , ?_assertEqual({{2012,12,17},{4,20,0}}, ezic:local_to_utc({{2012,12,17},{13,20,0}}, "Asia/Tokyo")) + , ?_assertEqual({{2012,12,17},{4,20,0}}, ezic:local_to_utc({{2012,12,16},{23,20,0}}, "America/Jamaica")) + , ?_assertEqual({{2012,12,17},{4,20,0}}, ezic:local_to_utc({{2012,12,16},{20,20,0}}, "America/Los_Angeles")) + ]. + + local_to_utc_errors_test_() -> [ ?_assertMatch({error, {ambiguous_zone, _}}, ezic:local_to_utc({{2010,11,7},{1,0,0}}, "America/Los_Angeles")) , ?_assertEqual({error, no_zone}, ezic:local_to_utc({{2010,3,14},{2,30,0}}, "America/Los_Angeles")) ]. -utc_to_local_smoke_test_() -> - [?_assertEqual({{2012,12,17},{13,20,0}}, ezic:utc_to_local({{2012,12,17},{4,20,0}}, "Asia/Tokyo")) + +%% Tests overlap logic around timezones that have odd DST rules +local_to_utc_funkyDST_test_() -> + [ + %% gap + ?_assertMatch({{1948,5,1},{16,0,0}}, ezic:local_to_utc({{1948,5,2},{1,00,00}}, "Asia/Tokyo")) + , ?_assertMatch({error, no_zone}, ezic:local_to_utc({{1948,5,2},{2,00,00}}, "Asia/Tokyo")) + , ?_assertMatch({{1948,5,1},{17,0,0}}, ezic:local_to_utc({{1948,5,2},{3,00,00}}, "Asia/Tokyo")) + + %% overlap + , ?_assertMatch({{1948,9,10},{14,30,0}}, ezic:local_to_utc({{1948,9,11},{0,30,00}}, "Asia/Tokyo")) + , ?_assertMatch({error, {ambiguous_zone, _}}, ezic:local_to_utc({{1948,9,11},{1,00,00}}, "Asia/Tokyo")) + , ?_assertMatch({{1948,9,10},{17,0,0}}, ezic:local_to_utc({{1948,9,11},{2,00,00}}, "Asia/Tokyo")) + + %% DST end + , ?_assertMatch({error, no_zone}, ezic:local_to_utc({{1951,5,6},{2,0,0}}, "Asia/Tokyo")) %% final DST gap + , ?_assertMatch({{1951,5,5},{17,0,0}}, ezic:local_to_utc({{1951,5,6},{3,0,0}}, "Asia/Tokyo")) %% DST in effect for the last time + , ?_assertMatch({error, {ambiguous_zone, _}}, ezic:local_to_utc({{1951,9,8},{1,0,0}}, "Asia/Tokyo")) %% DST overlap + , ?_assertMatch({{1951,9,7},{17,0,0}}, ezic:local_to_utc({{1951,9,8},{2,0,0}}, "Asia/Tokyo")) %% DST off for the last time + , ?_assertMatch({{1952,5,3},{17,0,0}}, ezic:local_to_utc({{1952,5,4},{2,0,0}}, "Asia/Tokyo")) %% ensure DST not in effect for 1952 ].