happy path tested! current times correctly reported for LA, NY, Tokyo, and Jamaica!

Japan doesn't observer DST. I didn't know that.
This commit is contained in:
aj
2010-11-04 04:20:38 -07:00
parent d48d32ddbb
commit 7dd76519a0
10 changed files with 236 additions and 23 deletions

2
.gitignore vendored
View File

@@ -5,4 +5,4 @@ tmp/
commit_msg
tzdata/
\#*
db/

View File

@@ -1,6 +1,6 @@
ERLC_WARNINGS := -W1
MNESIA_DIR := db
RUN_INIT :=
RUN_INIT := -run ezic_db init
DEBUG := +debug_info -Ddebug
TEST :=
TZSET := northamerica
@@ -32,7 +32,7 @@ build : DEBUG =
build : all
devstart : RUN_INIT = -s ezic dev_start
devstart : RUN_INIT := $(RUN_INIT) -s ezic dev_start
devstart : all run

View File

@@ -24,8 +24,10 @@
-ifdef(debug).
-define(debug(F,X), io:format("{~p:~p} - " ++ F ++ "~n", [?MODULE, ?LINE] ++ X)).
-define(debug(F), io:format("{~p:~p} - ~p~n", [?MODULE, ?LINE, F])).
-else.
-define(debug(F,X), void).
-define(debug(F), void).
-endif.

View File

@@ -1,11 +1,37 @@
-module(ezic).
-include("include/ezic.hrl").
-export([load/1, time2time/3]).
-export([dev_start/0]).
-export([localtime/1, time2time/3]).
-export([load/1, dev_start/0, test/0]).
load(Folder) ->
ezic_loader:load(Folder).
-define(gs2dt(X), calendar:gregorian_seconds_to_datetime(X)).
-define(dt2gs(X), calendar:datetime_to_gregorian_seconds(X)).
localtime(TzName) ->
Now= erlang:universaltime(),
Zones= ezic_db:zones(TzName),
% ?debug("All Zones: ~p", [Zones]),
CZone= ezic_zone:current(Now, Zones),
% ?debug("Current Zone: ~p", [CZone]),
RuleName= CZone#zone.rule,
Rules= ezic_db:rules(RuleName),
% ?debug("All Rules: ~p", [Rules]),
CRule= ezic_rule:current(Now, Rules),
% ?debug("Current Rule: ~p", [CRule]),
OffsetSec= ezic_zone:offset_sec(CZone),
DSTSec= ezic_rule:dst_sec(CRule),
%
SecDiff= OffsetSec + DSTSec,
?gs2dt(?dt2gs(Now) + SecDiff).
@@ -15,5 +41,19 @@ time2time(_DateTime, _FromTimeZone, _ToTimeZone) ->
load(Folder) ->
ezic_loader:load(Folder).
dev_start() ->
ezic:load(filename:join("priv","tzdata")).
test() ->
lists:foreach(
fun(TZ)->
io:format("~s: ~p~n", [TZ, localtime(TZ)])
end,
["Asia/Tokyo", "America/New_York", "America/Los_Angeles", "America/Jamaica"]).

View File

@@ -119,7 +119,6 @@ flatten_one(Zone=#zone{until=UTo}, NextZone=#zone{until=UFrom}, Rules) ->
flatten_each(Zone, Rule, From) ->
% ?debug("flatten_each: ~n\tZone: ~p~n\tRule: ~p~n\tFrom: ~p", [Zone, Rule, From]),
% erlang:error(dbg),
years
not_done.

View File

@@ -10,7 +10,7 @@
-export([for/2]).
-export([month_to_num/1, day_to_num/1]).
-export([compare_times/2, compare_datetimes/2, normalize/1, overlap/2]).
-export([compare_times/2, compare_datetimes/2, normalize/1, overlap/2, date_between/2]).
@@ -153,6 +153,9 @@ normalize({Y,M,{last, D}}) ->
{Date, #tztime{}};
normalize({Date={Y,M,D},Time={H,_,_}}) when is_integer(H) ->
{Date, #tztime{time=Time}};
normalize({Date={Y,M,D}, Tz=#tztime{}}) when is_integer(D) ->
{Date, Tz};
normalize({{Y,M,TzOn=#tzon{day=Day, filter=Filter}}, Tz=#tztime{}}) ->
@@ -181,6 +184,13 @@ dates_overlap_normal({D1s,D1e}, {D2s,D2e}) ->
orelse date_between_normal(D1e, {D2s,D2e}).
date_between(D1, {D2s, D2e}) ->
D1N= normalize(D1),
D2sN= normalize(D2s),
D2eN= normalize(D2e),
date_between_normal(D1N, {D2sN, D2eN}).
date_between_normal(minimum, {minimum,_}) -> true;
date_between_normal(minimum, {_,_}) -> false;

82
src/ezic_db.erl Normal file
View File

@@ -0,0 +1,82 @@
-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]).
zones(Name) ->
F = fun()->
Q = qlc:q([Z || Z=#zone{name=N} <- mnesia:table(zone), N=:=Name]),
qlc:e(Q)
end,
{atomic, Zones}= mnesia:transaction(F),
Zones.
rules(Name) ->
F = fun()->
Q = qlc:q([R || R=#rule{name=N} <- mnesia:table(rule), N=:=Name]),
qlc:e(Q)
end,
{atomic, Rules}= mnesia:transaction(F),
Rules.
-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(E={error, {_, {already_exists,_}}}) ->
mnesia:start(),
ok;
create_tabs(E) ->
?debug(E).
insert_all(Records) ->
mnesia:transaction(
fun() ->
lists:foreach(
fun(R)-> mnesia:write(R) end,
Records)
end).
getall(Tab) when is_atom(Tab) ->
F = fun() ->
Q = qlc:q([R || R<- mnesia:table(Tab)]),
qlc:e(Q)
end,
mnesia:transaction(F).

View File

@@ -14,8 +14,17 @@ load(File) ->
end
end,
{ok, Zones, Rules, Leaps, Links} = ezic_compile:separate(Records),
ezic_compile:flatten(Zones, Rules).
% @todo store in mnesia
% ezic_compile:flatten(Zones, Rules).
ezic_db:wipe(),
ezic_db:init(),
ezic_db:insert_all(Zones),
ezic_db:insert_all(Rules),
ezic_db:insert_all(Leaps),
ezic_db:insert_all(Links).

View File

@@ -2,19 +2,61 @@
-include("include/ezic.hrl").
-export([]).
-export([current/2, relevant/2, sort/2]).
-export([from_time/1, dst_sec/1]).
% returns a list of all {datetime,offset} sets that must be
% projected for this Rule.
project(Rule=#rule{from=F, to=only}) ->
not_done;
project(Rule=#rule{from=F, to=T}) ->
Dates= lists:map(fun(Y)-> ezic_date:for(Rule, Y) end, lists:seq(F, T)).
current(_, []) ->
none;
current(Now, Rules) ->
RelevantRules= lists:filter(fun(R)-> relevant(Now, R) end, Rules),
% ?debug("RelevantRules: ~p", [RelevantRules]),
SRules= lists:sort(fun sort/2, RelevantRules),
CRule= choose_rule(SRules),
CRule.
sort(R1, R2) ->
T1= from_time(R1),
T2= from_time(R2),
ezic_date:compare_datetimes(T1, T2).
relevant(Now, Rule=#rule{from=F, to=T}) ->
{{Y,_,_},_} = Now,
case ezic_date:date_between(Y, {F,T}) of
false -> false;
true -> relevant2(Now, Rule)
end.
relevant2(Now, Rule=#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;
choose_rule([H|_]) ->
H.
dst_sec(none) ->
0;
dst_sec(Rule) ->
calendar:time_to_seconds((Rule#rule.save)#tztime.time).
% returns all the years inbetween
years(_, Rule=#rule{from=F, to=only}) ->
[F];
years(Max, Rule=#rule{from=F, to=maximum}) ->
fudge.

29
src/ezic_zone.erl Normal file
View File

@@ -0,0 +1,29 @@
-module(ezic_zone).
-include("include/ezic.hrl").
-export([current/2, sort/2, revsort/2, offset_sec/1]).
current(_, []) ->
erlang:error(no_current);
current(Now, Zones) ->
SortedZones= lists:sort(fun revsort/2, Zones),
% ?debug("SortedZones: ~p", [SortedZones]),
[CZone|_]= lists:dropwhile(fun(SZ)-> older_zone(SZ, Now) end, SortedZones),
CZone.
sort(#zone{until=U1}, #zone{until=U2}) ->
ezic_date:compare_datetimes(U2, U1).
revsort(Z1=#zone{}, Z2=#zone{}) ->
not sort(Z1, Z2).
older_zone(Zone=#zone{until=Until}, Now) ->
ezic_date:compare_datetimes(Until, Now).
offset_sec(Zone) ->
calendar:time_to_seconds((Zone#zone.gmtoff)#tztime.time).