lots of work towards flattening the records. stalled at ezic_rule:next_event/4

This commit is contained in:
aj
2010-11-07 18:32:37 -08:00
parent c795abf2b6
commit 9ad8f3c826
12 changed files with 479 additions and 115 deletions

View File

@@ -17,7 +17,7 @@
% a flattened zone record, giving specific information for a specific time range. % a flattened zone record, giving specific information for a specific time range.
-record(flatzone, {tzname, tzrule, from, to, gmtoff}). -record(flatzone, {tzname, wall_from, wall_to, std_from, std_to, utc_from, utc_to, offset}).

122
notes/flattening.svg Normal file
View File

@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47pre4 r22446"
sodipodi:docname="flattening.svg">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="311.19013"
inkscape:cy="621.31928"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="750"
inkscape:window-x="-1"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#ff0000;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 83.571429,374.50504 607.142861,0"
id="path2818" />
<text
xml:space="preserve"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
x="34.285713"
y="288.07645"
id="text3604"
sodipodi:linespacing="100%"><tspan
sodipodi:role="line"
id="tspan3606"
x="34.285713"
y="288.07645">DST point changes</tspan></text>
<text
sodipodi:linespacing="100%"
id="text3608"
y="468.07645"
x="34.285713"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:DejaVu Sans Mono;-inkscape-font-specification:DejaVu Sans Mono"
xml:space="preserve"><tspan
y="468.07645"
x="34.285713"
id="tspan3610"
sodipodi:role="line">Zone point changes</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 84.285714,408.07647 217.142856,0"
id="path3612" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 262.85714,432.36218 462.71429,0"
id="path3614"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 137,340.36218 102,0"
id="path3668" />
<path
id="path3670"
d="m 217,348.36218 102,0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
id="path3674"
d="m 337,340.36218 102,0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 417,348.36218 102,0"
id="path3676" />
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 537,340.36218 102,0"
id="path3678" />
<path
id="path3680"
d="m 617,348.36218 102,0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -49,7 +49,8 @@ utc_to_local(UTCDatetime, TzName) ->
next_timechange(TzName) -> next_timechange(TzName) ->
next_timechange(erlang:universaltime(), TzName). next_timechange(erlang:universaltime(), TzName).
next_timechange(UtcDatetime, TzName) -> next_timechange(UTCDatetime, TzName) ->
UTCDatetime, TzName,
not_done. not_done.
@@ -63,7 +64,8 @@ next_timechange(UtcDatetime, TzName) ->
load(Folder) -> load(Folder) ->
ezic_loader:load(Folder). ezic_loader:load(Folder),
ezic_flatten:flatten().
dev_start() -> dev_start() ->
@@ -106,6 +108,6 @@ time_offset(Zone, Rule) ->
date_add(Datetime, SecDiff) -> date_add(Datetime, SecDiff) ->
?gs2dt(?dt2gs(Datetime) + SecDiff). ?gs2dt(?dt2gs(Datetime) + SecDiff).
date_subtract(Datetime, SecDiff) -> %% date_subtract(Datetime, SecDiff) ->
?gs2dt(?dt2gs(Datetime) - SecDiff). %% ?gs2dt(?dt2gs(Datetime) - SecDiff).

View File

@@ -8,10 +8,14 @@
-endif. -endif.
-export([for/2]). -export([for/2]).
-export([month_to_num/1, day_to_num/1]). -export([month_to_num/1, day_to_num/1]).
-export([compare_times/2, compare_datetimes/2, normalize/1, overlap/2, date_between/2]). -export([compare_times/2, compare_datetimes/2, normalize/1, overlap/2, date_between/2]).
-export([add_seconds/2, all_times/3]).
@@ -218,3 +222,52 @@ date_between_normal(D1, {D2s, D2e}) ->
andalso compare_datetimes_normal(D1, D2e). andalso compare_datetimes_normal(D1, D2e).
add_seconds(Datetime, Seconds) ->
calendar:gregorian_seconds_to_datetime(
calendar:date_to_gregorian_seconds(Datetime) + Seconds
).
% returns {WallTime, StdTime, UtcTime}
% where each is a datetime tuple: {{Y,M,D}{HH,MM,SS}}
% universal time given
all_times(Datetime=#tztime{time=UTCTime, flag=Flag}, Offset, DSTOffset)
when Flag=:=u; Flag=:=g; Flag=:=z ->
OSec= calendar:time_to_seconds(Offset),
DSTSec= calendar:time_to_seconds(DSTOffset),
STDTime= add_seconds(UTCTime, OSec),
WallTime= add_seconds(STDTime, DSTSec),
{WallTime, STDTime, UTCTime};
% standard time given
all_times(Datetime=#tztime{time=STDTime, flag=s}, Offset, DSTOffset) ->
OSec= calendar:time_to_seconds(Offset),
DSTSec= calendar:time_to_seconds(DSTOffset),
UTCTime= add_seconds(STDTime, -1*OSec),
WallTime= add_seconds(STDTime, DSTSec),
{WallTime, STDTime, UTCTime};
% wall time given
all_times(Datetime=#tztime{time=WallTime, flag=Flag}, Offset, DSTOffset)
when Flag=:=w, Flag=:=undefined ->
OSec= calendar:time_to_seconds(Offset),
DSTSec= calendar:time_to_seconds(DSTOffset),
STDTime= add_seconds(WallTime, -1*DSTSec),
UTCTime= add_seconds(STDTime, -1*OSec),
{WallTime, STDTime, UTCTime}.

View File

@@ -1,13 +1,28 @@
-module(ezic_db). -module(ezic_db).
-include("include/ezic.hrl"). -include("include/ezic.hrl").
-include_lib("stdlib/include/qlc.hrl"). -include_lib("stdlib/include/qlc.hrl").
-export([zones/1, rules/1]). -export([zones/1, rules/1]).
-export([wipe/0, init/0, insert_all/1, getall/1]). -export([wipe/0, init/0, insert_all/1, get_all/1]).
-define(create(Record),
{atomic, ok} = mnesia:create_table(Record,
[{type, bag}
, {disc_copies, [node()]}
, {attributes, record_info(fields, Record)}
])).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% READ - db reading methods
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% retrieve all zones by name
zones(Name) -> zones(Name) ->
F = fun()-> F = fun()->
Q = qlc:q([Z || Z=#zone{name=N} <- mnesia:table(zone), N=:=Name]), Q = qlc:q([Z || Z=#zone{name=N} <- mnesia:table(zone), N=:=Name]),
@@ -16,6 +31,7 @@ zones(Name) ->
{atomic, Zones}= mnesia:transaction(F), {atomic, Zones}= mnesia:transaction(F),
Zones. Zones.
% retrieve all rules by name
rules(Name) -> rules(Name) ->
F = fun()-> F = fun()->
Q = qlc:q([R || R=#rule{name=N} <- mnesia:table(rule), N=:=Name]), Q = qlc:q([R || R=#rule{name=N} <- mnesia:table(rule), N=:=Name]),
@@ -24,46 +40,22 @@ rules(Name) ->
{atomic, Rules}= mnesia:transaction(F), {atomic, Rules}= mnesia:transaction(F),
Rules. Rules.
% get all records from table
get_all(Tab) when is_atom(Tab) ->
F = fun() ->
Q = qlc:q([R || R<- mnesia:table(Tab)]),
qlc:e(Q)
end,
{atomic, Ret}= mnesia:transaction(F),
Ret.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% WRITE - insertion/edit methods
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-define(create(Record),
{atomic, ok} = mnesia:create_table(Record, [{type, bag}, {disc_copies, [node()]},{attributes, record_info(fields, Record)}])).
init() ->
create_tabs(mnesia:create_schema([node()])),
mnesia:wait_for_tables([rule, zone, link, leap], 3000).
wipe() ->
mnesia:stop(),
mnesia:delete_schema([node()]).
create_tabs(ok) ->
mnesia:start(),
?create(rule),
?create(zone),
?create(link),
?create(leap),
%% {atomic, ok}= mnesia:create_table(rule, [{disc_copies, [node()]},{attributes, record_info(fields, rule)}]),
%% {atomic, ok}= mnesia:create_table(zone, [{disc_copies, [node()]},{attributes, record_info(fields, zone)}]),
%% {atomic, ok}= mnesia:create_table(leap, [{disc_copies, [node()]},{attributes, record_info(fields, leap)}]),
%% {atomic, ok}= mnesia:create_table(link, [{disc_copies, [node()]},{attributes, record_info(fields, link)}]);
ok;
create_tabs({error, {_, {already_exists,_}}}) ->
mnesia:start(),
ok;
create_tabs(E) ->
?debug(E).
insert_all(Records) -> insert_all(Records) ->
mnesia:transaction( mnesia:transaction(
fun() -> fun() ->
@@ -74,10 +66,40 @@ insert_all(Records) ->
getall(Tab) when is_atom(Tab) ->
F = fun() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Q = qlc:q([R || R<- mnesia:table(Tab)]), % ADMIN - initialization and administration methods
qlc:e(Q) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end,
mnesia:transaction(F).
% initialize the db
init() ->
create_tabs(mnesia:create_schema([node()])),
mnesia:wait_for_tables([rule, zone, link, leap], 3000).
% WARNING: deletes all db files
wipe() ->
mnesia:stop(),
mnesia:delete_schema([node()]).
create_tabs(ok) ->
mnesia:start(),
?create(rule),
?create(zone),
?create(link),
?create(leap),
?create(flatzone),
ok;
create_tabs({error, {_, {already_exists,_}}}) ->
mnesia:start(),
ok;
create_tabs(E) ->
?debug(E).

145
src/ezic_flatten.erl Normal file
View File

@@ -0,0 +1,145 @@
-module(ezic_flatten).
-include("include/ezic.hrl").
-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(MINFLAT, ?FLAT(minimum,minimum,minimum)).
-export([flatten/0]).
flatten() ->
AllZones= ezic_db:get_all(zone),
flatten_zones(AllZones),
not_done.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PRIVATE
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
make_next_flat(#flatzone{wall_to=W, std_to=S, utc_to=U, offset=O}) ->
make_next_flat({W,S,U}, O);
make_next_flat(EndTimes) ->
make_next_flat(EndTimes, undefined).
make_next_flat({W,S,U}, O) ->
FW= ezic_date:add_seconds(W,1),
FS= ezic_date:add_seconds(S,1),
FU= ezic_date:add_seconds(U,1),
Flat= ?FLAT(FW, FS, FU),
Flat#flatzone{offset=O}.
end_flat(Flat=#flatzone{}, {W,S,U}) ->
?ENDFLAT(Flat,W,S,U).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INTERNAL
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% recursively processes sets of similar zones until they're all done, passing zone sets to flatten_zone_set/1
flatten_zones(Zones) ->
flatten_zones(Zones, []).
flatten_zones([], Flats) ->
Flats;
flatten_zones([Z1|_]= AllZones, Flats) ->
{CurrentZones, RestZones}= ezic_zone:split_by_name(Z1, AllZones),
SortedZones= lists:sort(fun ezic_zone:sort_ascending/2, CurrentZones),
NewFlats= lists:merge(Flats, flatten_zone_set(SortedZones)),
flatten_zones(RestZones, NewFlats).
% takes zones one-by-one, gathering relevant rules and create flat periods of the same gmt offset (#flatzone)
flatten_zone_set(Zones) ->
flatten_zone_set(?MINFLAT, Zones, []).
% flatten_zone_set(FromTime, Zones, Flats) -> [#flatzone{}]
% FromTime= #flatzone{*_from =/= undefined}
% Zones= [#zone{}]
% Flats= [#flatzone{}]
%
% assumes a new zone every time it is called
flatten_zone_set(_, [], Flats) ->
Flats;
flatten_zone_set(FromTimeStub, [Z1=#zone{rule=RuleName, until=UntilTime, gmtoff=Offset} | RestZones] = Zones, Flats) ->
%% we have a flatzone with start times, we populate the base offset
FromTime= FromTimeStub#flatzone{offset=Offset},
%% we gather all rules that _may_ apply (same year)
Rules= ezic_db:rules(RuleName),
RelevantRules= ezic_rule:filter(FromTime, UntilTime, Rules),
SortedRules= lists:sort(fun ezic_rule:sort_ascending/2, RelevantRules),
%% Then we plot points, bouncing from rule to rule, ending when the zone ends
{ok, NextFromTimeStub, NewFlats}= flatten_zone(FromTime, Z1, SortedRules),
not_done.
% flattens a single zone into multiple flatzones.
% FromTime has its *_from fields and offset field populated. (it should anyway)
% @todo invalid input checking
% Rules are sorted
flatten_zone(BaseFlat, Zone, Rules) ->
flatten_zone(BaseFlat, Zone, Rules, []).
flatten_zone(BaseFlat, Zone=#zone{until=Until, gmtoff=Offset}, Rules, Flats) ->
case ezic_rule:next_event(BaseFlat, Until, Offset, Rules) of
% is this enough information to compare all times corectly?
{end_until, EndTimes} ->
NewFlats= [end_flat(BaseFlat, EndTimes) | Flats],
NewFrom= make_next_flat(EndTimes),
{ok, NewFrom, NewFlats}; % base case
{end_rule, EndTimes, NextOffset} ->
NewFlats= [end_flat(BaseFlat, EndTimes) | Flats],
NewFrom= make_next_flat(EndTimes, NextOffset),
flatten_zone(NewFrom, Zone, Rules, NewFlats) % tail recursion
end.

View File

@@ -15,8 +15,6 @@ load(File) ->
end, end,
{ok, Zones, Rules, Leaps, Links} = ezic_compile:separate(Records), {ok, Zones, Rules, Leaps, Links} = ezic_compile:separate(Records),
% ezic_compile:flatten(Zones, Rules).
ezic_db:wipe(), ezic_db:wipe(),
ezic_db:init(), ezic_db:init(),
@@ -30,6 +28,7 @@ load(File) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% INTERNAL % INTERNAL
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -38,7 +37,6 @@ load(File) ->
% returns a list of tzdata records for all files in folder % returns a list of tzdata records for all files in folder
% note: assumes every file in the folder is a tzdata file % note: assumes every file in the folder is a tzdata file
% this method does not recurse.
parse_dir(Folder) -> parse_dir(Folder) ->
{ok, Files} = file:list_dir(Folder), {ok, Files} = file:list_dir(Folder),
lists:flatten([ parse_file(filename:join(Folder,File)) || File <- Files ]). lists:flatten([ parse_file(filename:join(Folder,File)) || File <- Files ]).
@@ -52,24 +50,33 @@ parse_file(File) ->
parse_lines(eof, _, Records) -> parse_lines(eof, _, Records) ->
Records; Records;
parse_lines({ok, Line}, File, Records) -> parse_lines({ok, Line}, File, Records) ->
StrLine= clean_line(Line), StrLine= clean_line(Line),
case length(StrLine) > 0 of NewRecords= case length(StrLine) > 0 of
false -> parse_lines(file:read_line(File), File, Records); false -> Records;
true -> parse_lines_2(StrLine, File, Records) true -> [parse_to_record(StrLine, File, Records) | Records]
end. end,
parse_lines(file:read_line(File), File, NewRecords).
parse_lines_2(Line, File, Records) ->
%?debug("Line: ~p", [Line]), % the Line has data, so we parse it, build a record, and return it
parse_to_record(Line, File, Records) ->
[Type | Data] = string:tokens(Line, " \t"), [Type | Data] = string:tokens(Line, " \t"),
{ok, PrevType, PrevName} = prev_rec_type(Records), {ok, PrevType, PrevName} = prev_rec_type(Records),
{ok, Record} = build_record(Type, Data, {PrevType, PrevName}), {ok, Record} = build_record(Type, Data, {PrevType, PrevName}),
parse_lines(file:read_line(File), File, [Record|Records]).
% parse_lines(file:read_line(File), File, [Record|Records]).
Record.
% retrieves the previous record type, in case Zone continuations occur (multiline)
prev_rec_type([]) -> prev_rec_type([]) ->
{ok, null, null}; {ok, null, null};
prev_rec_type([#zone{name=Name}|_]) -> prev_rec_type([#zone{name=Name}|_]) ->
@@ -77,6 +84,9 @@ prev_rec_type([#zone{name=Name}|_]) ->
prev_rec_type(List) when is_list(List) -> prev_rec_type(List) when is_list(List) ->
{ok, void, void}. {ok, void, void}.
clean_line(Line) -> clean_line(Line) ->
Line1= string:strip(Line), Line1= string:strip(Line),
Line2= string:strip(Line1, both, $\n), Line2= string:strip(Line1, both, $\n),
@@ -105,3 +115,6 @@ build_record("Leap", Data,_) ->
build_record(Type, Data, PT) -> build_record(Type, Data, PT) ->
{error, {badLine, Type, Data, PT}}. {error, {badLine, Type, Data, PT}}.

View File

@@ -136,6 +136,7 @@ parse_save(X) ->
parse_save_rev(XRev). parse_save_rev(XRev).
parse_time("-") -> parse_time("-") ->
#tztime{time={0,0,0}}; #tztime{time={0,0,0}};
parse_time(TS) -> parse_time(TS) ->

View File

@@ -2,62 +2,39 @@
-include("include/ezic.hrl"). -include("include/ezic.hrl").
-export([current/2, current_set/2, sort/2]). -export([next_event/4]).
-export([from_time/1, dst_sec/1]).
current(_, []) ->
none;
current(Now, Rules) ->
Current_SetRules= lists:filter(fun(R)-> current_set(Now, R) end, Rules),
% ?debug("Current_SetRules: ~p", [Current_SetRules]),
SRules= lists:sort(fun sort/2, Current_SetRules),
CRule= choose_rule(SRules),
CRule.
% newest first % determines the next zone or dst change event
sort(R1, R2) -> % returns the latest possible times the previous period is in effect (`Until -1sec` in most cases)
T1= from_time(R1), % Rules are sorted
T2= from_time(R2), next_event(BaseFlat, ZoneEnd, Offset, []) ->
ezic_date:compare_datetimes(T2, T1). {end_until, make_times(ZoneEnd, Offset)};
% BaseFlat has offset defined
% @todo ensure offset defined on BaseFlat
% Rules are sorted in ascending order (oldest first)
next_event(BaseFlat=#flatzone{utc_from=#tztime{time={{Year,_,_},_}}},
ZoneEnd, ZoneOffset, [Rule|RestRules]) ->
not_done.
current_set(Now, Rule=#rule{from=F, to=T}) ->
{{Y,_,_},_} = Now,
case ezic_date:date_between(Y, {F,T}) of
false -> false;
true -> current_set2(Now, Rule)
end.
current_set2(Now, #rule{in=Month, on=Day, at=Time}) ->
{{Y,_,_},_} = Now,
RTime= {{Y, Month, Day}, Time},
ezic_date:compare_datetimes(RTime, Now).
from_time(#rule{from=F, in=M, on=O, at=A}) ->
{{F,M,O},A}.
choose_rule([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
none; % PRIVATE
choose_rule([H|_]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
H.
% create endtimes for a #flatzone, with no dst offset
make_times(Until, Offset) ->
make_times(Until, Offset, 0).
make_times(Until, Offset, DSTOffset) ->
ezic_date:all_times(Until, Offset, DSTOffset).
dst_sec(none) ->
0;
dst_sec(Rule) ->
calendar:time_to_seconds((Rule#rule.save)#tztime.time).

View File

@@ -1,11 +1,13 @@
-module(ezic_zone). -module(ezic_zone).
-include("include/ezic.hrl"). -include("include/ezic.hrl").
-include_lib("eunit/include/eunit.hrl").
-export([ -export([
current/1 current/1
, current_as_of_utc/2 , current_as_of_utc/2
, sort/2 , split_by_name/2
, revsort/2 , sort_ascending/2
, sort_descending/2
, offset_sec/1 , offset_sec/1
]). ]).
@@ -28,8 +30,8 @@ current_as_of_utc(UTCDatetime, TzName) ->
get_zone_utc(_, []) -> get_zone_utc(_, []) ->
erlang:error(no_current); erlang:error(no_current);
get_zone_utc(UTCDatetime, Zones) -> get_zone_utc(_UTCDatetime, Zones) ->
SortedZones= lists:sort(fun revsort/2, Zones), _SortedZones= lists:sort(fun sort_descending/2, Zones),
%% [CZone|_]= lists:dropwhile(fun(SZ)-> older_zone_utc(SZ, UTCDatetime) end, SortedZones), %% [CZone|_]= lists:dropwhile(fun(SZ)-> older_zone_utc(SZ, UTCDatetime) end, SortedZones),
%% CZone. %% CZone.
@@ -43,17 +45,29 @@ get_zone_utc(UTCDatetime, Zones) ->
sort(#zone{until=U1}, #zone{until=U2}) ->
% returns {[SimilarZone], [DifferentZone]}
% where Similar Zones are those with the same name as Zone
% and DifferentZones are all the rest
split_by_name(#zone{name=N}, Zones) ->
lists:partition(
fun(#zone{name=NI}) ->
N =:= NI
end
, Zones).
sort_descending(#zone{until=U1}, #zone{until=U2}) ->
ezic_date:compare_datetimes(U2, U1). ezic_date:compare_datetimes(U2, U1).
revsort(Z1=#zone{}, Z2=#zone{}) -> sort_ascending(Z1=#zone{}, Z2=#zone{}) ->
not sort(Z1, Z2). not sort_descending(Z1, Z2).
% @bug doesn't normalize times. Times are self-relative (to standard time), and Now may come from any timezone unless we're careful about that. % @bug doesn't normalize times. Times are self-relative (to standard time), and Now may come from any timezone unless we're careful about that.
older_zone(#zone{until=Until}, UTCDatetime) -> %% older_zone(#zone{until=Until}, UTCDatetime) ->
ezic_date:compare_datetimes(Until, UTCDatetime). %% ezic_date:compare_datetimes(Until, UTCDatetime).

14
test/ezic_zone_tests.erl Normal file
View File

@@ -0,0 +1,14 @@
-module(ezic_zone_tests).
-include("include/ezic.hrl").
-include_lib("eunit/include/eunit.hrl").
split_by_name_test_() ->
AZones= [A=#zone{name=a}, #zone{name=a}, #zone{name=a}],
BZones= [B=#zone{name=b}, #zone{name=b}, #zone{name=b}],
AllZones= lists:merge(AZones, BZones),
[
?_assertEqual({AZones, BZones}, ezic_zone:split_by_name(A, AllZones))
, ?_assertEqual({BZones, AZones}, ezic_zone:split_by_name(B, AllZones))
, ?_assertEqual({[], AllZones}, ezic_zone:split_by_name(#zone{name=c}, AllZones))
].

View File

@@ -4,4 +4,5 @@
all() -> all() ->
eunit:test(ezic_record), eunit:test(ezic_record),
eunit:test(ezic_date). eunit:test(ezic_date),
eunit:test(ezic_zone).