lots of work towards flattening the records. stalled at ezic_rule:next_event/4
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
|
||||
% 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
122
notes/flattening.svg
Normal 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 |
10
src/ezic.erl
10
src/ezic.erl
@@ -49,7 +49,8 @@ utc_to_local(UTCDatetime, TzName) ->
|
||||
next_timechange(TzName) ->
|
||||
next_timechange(erlang:universaltime(), TzName).
|
||||
|
||||
next_timechange(UtcDatetime, TzName) ->
|
||||
next_timechange(UTCDatetime, TzName) ->
|
||||
UTCDatetime, TzName,
|
||||
not_done.
|
||||
|
||||
|
||||
@@ -63,7 +64,8 @@ next_timechange(UtcDatetime, TzName) ->
|
||||
|
||||
|
||||
load(Folder) ->
|
||||
ezic_loader:load(Folder).
|
||||
ezic_loader:load(Folder),
|
||||
ezic_flatten:flatten().
|
||||
|
||||
|
||||
dev_start() ->
|
||||
@@ -106,6 +108,6 @@ time_offset(Zone, Rule) ->
|
||||
date_add(Datetime, SecDiff) ->
|
||||
?gs2dt(?dt2gs(Datetime) + SecDiff).
|
||||
|
||||
date_subtract(Datetime, SecDiff) ->
|
||||
?gs2dt(?dt2gs(Datetime) - SecDiff).
|
||||
%% date_subtract(Datetime, SecDiff) ->
|
||||
%% ?gs2dt(?dt2gs(Datetime) - SecDiff).
|
||||
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
-endif.
|
||||
|
||||
|
||||
|
||||
|
||||
-export([for/2]).
|
||||
-export([month_to_num/1, day_to_num/1]).
|
||||
-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).
|
||||
|
||||
|
||||
|
||||
|
||||
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}.
|
||||
|
||||
|
||||
|
||||
108
src/ezic_db.erl
108
src/ezic_db.erl
@@ -1,13 +1,28 @@
|
||||
|
||||
-module(ezic_db).
|
||||
-include("include/ezic.hrl").
|
||||
-include_lib("stdlib/include/qlc.hrl").
|
||||
|
||||
|
||||
-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) ->
|
||||
F = fun()->
|
||||
Q = qlc:q([Z || Z=#zone{name=N} <- mnesia:table(zone), N=:=Name]),
|
||||
@@ -16,6 +31,7 @@ zones(Name) ->
|
||||
{atomic, Zones}= mnesia:transaction(F),
|
||||
Zones.
|
||||
|
||||
% retrieve all rules by name
|
||||
rules(Name) ->
|
||||
F = fun()->
|
||||
Q = qlc:q([R || R=#rule{name=N} <- mnesia:table(rule), N=:=Name]),
|
||||
@@ -24,46 +40,22 @@ rules(Name) ->
|
||||
{atomic, Rules}= mnesia:transaction(F),
|
||||
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) ->
|
||||
mnesia:transaction(
|
||||
fun() ->
|
||||
@@ -74,10 +66,40 @@ insert_all(Records) ->
|
||||
|
||||
|
||||
|
||||
getall(Tab) when is_atom(Tab) ->
|
||||
F = fun() ->
|
||||
Q = qlc:q([R || R<- mnesia:table(Tab)]),
|
||||
qlc:e(Q)
|
||||
end,
|
||||
mnesia:transaction(F).
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% ADMIN - initialization and administration methods
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
|
||||
% 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
145
src/ezic_flatten.erl
Normal 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.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ load(File) ->
|
||||
end,
|
||||
{ok, Zones, Rules, Leaps, Links} = ezic_compile:separate(Records),
|
||||
|
||||
% ezic_compile:flatten(Zones, Rules).
|
||||
|
||||
ezic_db:wipe(),
|
||||
ezic_db:init(),
|
||||
|
||||
@@ -30,6 +28,7 @@ load(File) ->
|
||||
|
||||
|
||||
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% INTERNAL
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
@@ -38,7 +37,6 @@ load(File) ->
|
||||
|
||||
% returns a list of tzdata records for all files in folder
|
||||
% note: assumes every file in the folder is a tzdata file
|
||||
% this method does not recurse.
|
||||
parse_dir(Folder) ->
|
||||
{ok, Files} = file:list_dir(Folder),
|
||||
lists:flatten([ parse_file(filename:join(Folder,File)) || File <- Files ]).
|
||||
@@ -52,24 +50,33 @@ parse_file(File) ->
|
||||
|
||||
|
||||
|
||||
|
||||
parse_lines(eof, _, Records) ->
|
||||
Records;
|
||||
parse_lines({ok, Line}, File, Records) ->
|
||||
StrLine= clean_line(Line),
|
||||
case length(StrLine) > 0 of
|
||||
false -> parse_lines(file:read_line(File), File, Records);
|
||||
true -> parse_lines_2(StrLine, File, Records)
|
||||
end.
|
||||
NewRecords= case length(StrLine) > 0 of
|
||||
false -> Records;
|
||||
true -> [parse_to_record(StrLine, File, Records) | Records]
|
||||
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"),
|
||||
{ok, PrevType, PrevName} = prev_rec_type(Records),
|
||||
{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([]) ->
|
||||
{ok, null, null};
|
||||
prev_rec_type([#zone{name=Name}|_]) ->
|
||||
@@ -77,6 +84,9 @@ prev_rec_type([#zone{name=Name}|_]) ->
|
||||
prev_rec_type(List) when is_list(List) ->
|
||||
{ok, void, void}.
|
||||
|
||||
|
||||
|
||||
|
||||
clean_line(Line) ->
|
||||
Line1= string:strip(Line),
|
||||
Line2= string:strip(Line1, both, $\n),
|
||||
@@ -105,3 +115,6 @@ build_record("Leap", Data,_) ->
|
||||
build_record(Type, Data, PT) ->
|
||||
{error, {badLine, Type, Data, PT}}.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -136,6 +136,7 @@ parse_save(X) ->
|
||||
parse_save_rev(XRev).
|
||||
|
||||
|
||||
|
||||
parse_time("-") ->
|
||||
#tztime{time={0,0,0}};
|
||||
parse_time(TS) ->
|
||||
|
||||
@@ -2,62 +2,39 @@
|
||||
-include("include/ezic.hrl").
|
||||
|
||||
|
||||
-export([current/2, current_set/2, sort/2]).
|
||||
-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.
|
||||
-export([next_event/4]).
|
||||
|
||||
|
||||
|
||||
|
||||
% newest first
|
||||
sort(R1, R2) ->
|
||||
T1= from_time(R1),
|
||||
T2= from_time(R2),
|
||||
ezic_date:compare_datetimes(T2, T1).
|
||||
% determines the next zone or dst change event
|
||||
% returns the latest possible times the previous period is in effect (`Until -1sec` in most cases)
|
||||
% Rules are sorted
|
||||
next_event(BaseFlat, ZoneEnd, Offset, []) ->
|
||||
{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]) ->
|
||||
|
||||
|
||||
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}.
|
||||
not_done.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
choose_rule([]) ->
|
||||
none;
|
||||
choose_rule([H|_]) ->
|
||||
H.
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
% PRIVATE
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
% 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).
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
-module(ezic_zone).
|
||||
-include("include/ezic.hrl").
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-export([
|
||||
current/1
|
||||
, current_as_of_utc/2
|
||||
, sort/2
|
||||
, revsort/2
|
||||
, split_by_name/2
|
||||
, sort_ascending/2
|
||||
, sort_descending/2
|
||||
, offset_sec/1
|
||||
]).
|
||||
|
||||
@@ -28,8 +30,8 @@ current_as_of_utc(UTCDatetime, TzName) ->
|
||||
|
||||
get_zone_utc(_, []) ->
|
||||
erlang:error(no_current);
|
||||
get_zone_utc(UTCDatetime, Zones) ->
|
||||
SortedZones= lists:sort(fun revsort/2, Zones),
|
||||
get_zone_utc(_UTCDatetime, Zones) ->
|
||||
_SortedZones= lists:sort(fun sort_descending/2, Zones),
|
||||
%% [CZone|_]= lists:dropwhile(fun(SZ)-> older_zone_utc(SZ, UTCDatetime) end, SortedZones),
|
||||
%% 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).
|
||||
|
||||
revsort(Z1=#zone{}, Z2=#zone{}) ->
|
||||
not sort(Z1, Z2).
|
||||
sort_ascending(Z1=#zone{}, Z2=#zone{}) ->
|
||||
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.
|
||||
older_zone(#zone{until=Until}, UTCDatetime) ->
|
||||
ezic_date:compare_datetimes(Until, UTCDatetime).
|
||||
%% older_zone(#zone{until=Until}, UTCDatetime) ->
|
||||
%% ezic_date:compare_datetimes(Until, UTCDatetime).
|
||||
|
||||
|
||||
|
||||
|
||||
14
test/ezic_zone_tests.erl
Normal file
14
test/ezic_zone_tests.erl
Normal 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))
|
||||
].
|
||||
@@ -4,4 +4,5 @@
|
||||
|
||||
all() ->
|
||||
eunit:test(ezic_record),
|
||||
eunit:test(ezic_date).
|
||||
eunit:test(ezic_date),
|
||||
eunit:test(ezic_zone).
|
||||
|
||||
Reference in New Issue
Block a user