Add timezone server basics with some tests
This commit is contained in:
parent
1391f38558
commit
642ef7a04c
6 changed files with 252 additions and 15 deletions
|
@ -9,5 +9,5 @@
|
|||
|
||||
{deps, [
|
||||
{erlware_commons, ".*", {git, "git://github.com/erlware/erlware_commons.git", "HEAD"}},
|
||||
{erlang_localtime, ".*", {git, "git://github.com/dmitryme/erlang_localtime.git", "HEAD"}}
|
||||
{erlang_localtime, ".*", {git, "git://github.com/choptastic/erlang_localtime.git", "HEAD"}}
|
||||
]}.
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
kernel,
|
||||
stdlib
|
||||
]},
|
||||
{mod, { qdate, []}},
|
||||
{mod, { qdate_app, []}},
|
||||
{env, []}
|
||||
]}.
|
||||
|
|
104
src/qdate.erl
104
src/qdate.erl
|
@ -21,12 +21,14 @@
|
|||
%% register_format/1
|
||||
%% ]).
|
||||
%%
|
||||
%% -export([
|
||||
%% set_timezone/1,
|
||||
%% set_timezone/2,
|
||||
%% get_timezone/0,
|
||||
%% get_timezone/1
|
||||
%% ]).
|
||||
-export([
|
||||
set_timezone/1,
|
||||
set_timezone/2,
|
||||
get_timezone/0,
|
||||
get_timezone/1,
|
||||
clear_timezone/0,
|
||||
clear_timezone/1
|
||||
]).
|
||||
|
||||
|
||||
%% Exported for API compatibility with ec_date
|
||||
|
@ -52,11 +54,14 @@
|
|||
TZ -> TZ
|
||||
end).
|
||||
|
||||
-define(DETERMINE_TZ, determine_timezone()).
|
||||
|
||||
|
||||
to_string(Format) ->
|
||||
to_string(Format, now()).
|
||||
|
||||
to_string(Format, Date) ->
|
||||
to_string(Format, ?DEFAULT_TZ, Date).
|
||||
to_string(Format, ?DETERMINE_TZ, Date).
|
||||
|
||||
to_string(Format, ToTZ, Date) ->
|
||||
ec_date:format(Format,to_date(Date,ToTZ)).
|
||||
|
@ -85,28 +90,51 @@ raw_to_date(Date = {{_,_,_},{_,_,_}}) ->
|
|||
Date.
|
||||
|
||||
to_date(RawDate) ->
|
||||
to_date(RawDate, ?DEFAULT_TZ).
|
||||
to_date(RawDate, ?DETERMINE_TZ).
|
||||
|
||||
to_date(RawDate, ToTZKey) when is_atom(ToTZKey) orelse is_tuple(ToTZKey) ->
|
||||
case get_timezone(ToTZKey) of
|
||||
undefined -> throw({timezone_key_not_found,ToTZKey});
|
||||
ToTZ -> to_date(RawDate, ToTZ)
|
||||
end;
|
||||
to_date(RawDate, ToTZ) ->
|
||||
{RawDate2,FromTZ} = extract_timezone(RawDate),
|
||||
Date = raw_to_date(RawDate2),
|
||||
localtime:local_to_local(Date,FromTZ,ToTZ).
|
||||
|
||||
set_timezone(TZ) ->
|
||||
qdate_srv:set_timezone(TZ).
|
||||
|
||||
set_timezone(Key,TZ) ->
|
||||
qdate_srv:set_timezone(Key, TZ).
|
||||
|
||||
get_timezone() ->
|
||||
qdate_srv:get_timezone().
|
||||
|
||||
get_timezone(Key) ->
|
||||
qdate_srv:get_timezone(Key).
|
||||
|
||||
clear_timezone() ->
|
||||
qdate_srv:clear_timezone().
|
||||
|
||||
clear_timezone(Key) ->
|
||||
qdate_srv:clear_timezone(Key).
|
||||
|
||||
extract_timezone(Unixtime) when is_integer(Unixtime) ->
|
||||
{Unixtime, ?DEFAULT_TZ};
|
||||
{Unixtime, ?DETERMINE_TZ};
|
||||
extract_timezone(DateString) when is_list(DateString) ->
|
||||
AllTimezones = localtime:list_timezones(),
|
||||
RevDate = lists:reverse(DateString),
|
||||
extract_timezone_helper(RevDate, AllTimezones);
|
||||
extract_timezone(Date={{_,_,_},{_,_,_}}) ->
|
||||
{Date, ?DEFAULT_TZ};
|
||||
{Date, ?DETERMINE_TZ};
|
||||
extract_timezone(Now={_,_,_}) ->
|
||||
{Now, ?DEFAULT_TZ};
|
||||
{Now, ?DETERMINE_TZ};
|
||||
extract_timezone({MiscDate,TZ}) ->
|
||||
{MiscDate,TZ}.
|
||||
|
||||
extract_timezone_helper(RevDate, []) ->
|
||||
{lists:reverse(RevDate), ?DEFAULT_TZ};
|
||||
{lists:reverse(RevDate), ?DETERMINE_TZ};
|
||||
extract_timezone_helper(RevDate, [TZ | TZs]) ->
|
||||
RevTZ = lists:reverse(TZ),
|
||||
case lists:split(length(TZ),RevDate) of
|
||||
|
@ -116,6 +144,12 @@ extract_timezone_helper(RevDate, [TZ | TZs]) ->
|
|||
extract_timezone_helper(RevDate, TZs)
|
||||
end.
|
||||
|
||||
determine_timezone() ->
|
||||
case qdate_srv:get_timezone() of
|
||||
undefined -> ?DEFAULT_TZ;
|
||||
TZ -> TZ
|
||||
end.
|
||||
|
||||
to_unixtime(Unixtime) when is_integer(Unixtime) ->
|
||||
Unixtime;
|
||||
to_unixtime({MegaSecs,Secs,_}) ->
|
||||
|
@ -156,7 +190,36 @@ floor(N) when N < 0 ->
|
|||
%% TESTS
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
all_test_() ->
|
||||
%% emulates as if a forum-type website has a Site tz, and a user-specified tz
|
||||
-define(SITE_TZ,"PST").
|
||||
-define(USER_TZ,"CST").
|
||||
-define(SELF_TZ,"EST"). %% Self will be the pid of the current running process
|
||||
-define(SITE_KEY,test_site_key).
|
||||
-define(USER_KEY,test_user_key).
|
||||
|
||||
|
||||
|
||||
tz_test_() ->
|
||||
{
|
||||
setup,
|
||||
fun start_test/0,
|
||||
fun stop_test/1,
|
||||
fun(SetupData) ->
|
||||
{spawn,[
|
||||
simple_test(),
|
||||
tz_tests(SetupData)
|
||||
]}
|
||||
end
|
||||
}.
|
||||
|
||||
tz_tests(_) ->
|
||||
[
|
||||
?_assertEqual(?SELF_TZ,begin set_timezone(?SELF_TZ),get_timezone() end),
|
||||
?_assertEqual(?USER_TZ,get_timezone(?USER_KEY)),
|
||||
?_assertEqual(?SITE_TZ,get_timezone(?SITE_KEY))
|
||||
].
|
||||
|
||||
simple_test() ->
|
||||
[
|
||||
?_assertEqual(0,to_unixtime({0,0,0})),
|
||||
?_assertEqual({0,0,0},to_now(0)),
|
||||
|
@ -171,3 +234,18 @@ all_test_() ->
|
|||
?_assertEqual({{2013,1,1},{0,15,15}},to_date("December 31, 2012 6:15:15pm CST","GMT"))
|
||||
].
|
||||
|
||||
%%tz_char_tests(_) ->
|
||||
%% qdate:set_timezone(?SELF_TZ),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
start_test() ->
|
||||
application:start(qdate),
|
||||
qdate:set_timezone(?SITE_KEY,?SITE_TZ),
|
||||
qdate:set_timezone(?USER_KEY,?USER_TZ).
|
||||
|
||||
stop_test(_) ->
|
||||
ok.
|
||||
|
|
16
src/qdate_app.erl
Normal file
16
src/qdate_app.erl
Normal file
|
@ -0,0 +1,16 @@
|
|||
-module(qdate_app).
|
||||
|
||||
-behaviour(application).
|
||||
|
||||
%% Application callbacks
|
||||
-export([start/2, stop/1]).
|
||||
|
||||
%% ===================================================================
|
||||
%% Application callbacks
|
||||
%% ===================================================================
|
||||
|
||||
start(_StartType, _StartArgs) ->
|
||||
qdate_sup:start_link().
|
||||
|
||||
stop(_State) ->
|
||||
ok.
|
114
src/qdate_srv.erl
Normal file
114
src/qdate_srv.erl
Normal file
|
@ -0,0 +1,114 @@
|
|||
-module(qdate_srv).
|
||||
-behaviour(gen_server).
|
||||
|
||||
-define(SRV, ?MODULE).
|
||||
|
||||
-export([
|
||||
start_link/0,
|
||||
init/1,
|
||||
handle_call/3,
|
||||
handle_cast/2,
|
||||
handle_info/2,
|
||||
code_change/3,
|
||||
terminate/2
|
||||
]).
|
||||
|
||||
-export([
|
||||
set_timezone/1,
|
||||
set_timezone/2,
|
||||
|
||||
get_timezone/0,
|
||||
get_timezone/1,
|
||||
|
||||
clear_timezone/0,
|
||||
clear_timezone/1
|
||||
|
||||
%% register_parser/1,
|
||||
%% register_parser/2,
|
||||
%%
|
||||
%% deregister_parsers/0,
|
||||
%% deregister_parsers/1,
|
||||
%%
|
||||
%% register_format/1,
|
||||
%% register_format/2,
|
||||
%%
|
||||
%% deregister_format/0,
|
||||
%% deregister_format/1
|
||||
]).
|
||||
|
||||
%% PUBLIC API FUNCTIONS
|
||||
|
||||
start_link() ->
|
||||
gen_server:start_link({local, ?SRV}, ?MODULE, [], []).
|
||||
|
||||
set_timezone(TZ) ->
|
||||
set_timezone(self(),TZ).
|
||||
|
||||
set_timezone(Key,TZ) ->
|
||||
gen_server:call(?SRV,{set_timezone,Key,TZ}).
|
||||
|
||||
get_timezone() ->
|
||||
get_timezone(self()).
|
||||
|
||||
get_timezone(Key) ->
|
||||
gen_server:call(?SRV,{get_timezone,Key}).
|
||||
|
||||
clear_timezone() ->
|
||||
clear_timezone(self()).
|
||||
|
||||
clear_timezone(Key) ->
|
||||
gen_server:call(?SRV, {clear_timezone, Key}).
|
||||
|
||||
%% SERVER FUNCTIONS
|
||||
|
||||
-record(state, {tz, parsers, formats}).
|
||||
|
||||
init(_) ->
|
||||
State = #state{tz=dict:new(),parsers=dict:new(),formats=dict:new()},
|
||||
{ok, State}.
|
||||
|
||||
handle_cast(_,State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_info({'DOWN', MonitorRef, process, Pid, _Reason}, State) ->
|
||||
erlang:demonitor(MonitorRef),
|
||||
NewTZ = dict:erase(Pid, State#state.tz),
|
||||
NewParsers = dict:erase(Pid, State#state.parsers),
|
||||
NewFormats = dict:erase(Pid, State#state.formats),
|
||||
NewState = State#state{tz=NewTZ, parsers=NewParsers, formats=NewFormats},
|
||||
{noreply, NewState };
|
||||
handle_info(_, State) ->
|
||||
{noreply, State}.
|
||||
|
||||
handle_call({set_timezone,Key,TZ}, _From, State) ->
|
||||
monitor_if_pid(Key),
|
||||
NewTZ = dict:store(Key, TZ, State#state.tz),
|
||||
NewState = State#state{tz=NewTZ},
|
||||
{reply, ok, NewState};
|
||||
handle_call({clear_timezone,Key},_From, State) ->
|
||||
NewTZ = dict:erase(Key, State#state.tz),
|
||||
NewState = State#state{tz=NewTZ},
|
||||
{reply, ok, NewState};
|
||||
handle_call({get_timezone,Key},_From, State) ->
|
||||
Reply = case dict:find(Key, State#state.tz) of
|
||||
error -> undefined;
|
||||
{ok, Value} -> Value
|
||||
end,
|
||||
{reply, Reply, State}.
|
||||
%% handle_call({register_parser,Key,Parser}) ->
|
||||
%% handle_call({deregister_parsers,Key}) ->
|
||||
%% handle_call({register_format,Key,Format}) ->
|
||||
%% handle_call({deregister_formats,Key}) ->
|
||||
|
||||
terminate(_Reason, _State) ->
|
||||
ok.
|
||||
|
||||
code_change(_OldVersion, State, _Extra) ->
|
||||
{ok, State}.
|
||||
|
||||
%% PRIVATE TOOLS
|
||||
|
||||
monitor_if_pid(Key) when is_pid(Key) ->
|
||||
erlang:monitor(process,Key);
|
||||
monitor_if_pid(_) ->
|
||||
do_nothing.
|
29
src/qdate_sup.erl
Normal file
29
src/qdate_sup.erl
Normal file
|
@ -0,0 +1,29 @@
|
|||
|
||||
-module(qdate_sup).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
%% API
|
||||
-export([start_link/0]).
|
||||
|
||||
%% Supervisor callbacks
|
||||
-export([init/1]).
|
||||
|
||||
%% Helper macro for declaring children of supervisor
|
||||
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
|
||||
|
||||
%% ===================================================================
|
||||
%% API functions
|
||||
%% ===================================================================
|
||||
|
||||
start_link() ->
|
||||
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
||||
|
||||
%% ===================================================================
|
||||
%% Supervisor callbacks
|
||||
%% ===================================================================
|
||||
|
||||
init([]) ->
|
||||
Server = ?CHILD(qdate_srv, worker),
|
||||
{ok, { {one_for_one, 5, 10}, [Server]} }.
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue