diff --git a/src/qdate.erl b/src/qdate.erl index 4661f4c..be606d0 100644 --- a/src/qdate.erl +++ b/src/qdate.erl @@ -20,6 +20,11 @@ unixtime/0 ]). +-export([ + compare/2, + compare/3 +]). + -export([ register_parser/2, register_parser/1, @@ -69,6 +74,7 @@ start() -> stop() -> application:stop(qdate). + to_string(Format) -> to_string(Format, os:timestamp()). @@ -143,14 +149,6 @@ leading_zero(I) when I < 10 -> leading_zero(I) -> integer_to_list(I). -get_timezone_shift(TZ, Date) -> - case localtime:tz_shift(Date, TZ) of - unable_to_detect -> {error,unable_to_detect}; - {error,T} -> {error,T}; - {Sh, _DstSh} -> Sh; - Sh -> Sh - end. - format(Format) -> to_string(Format). @@ -193,6 +191,93 @@ to_date(ToTZ, RawDate) -> end end. +%% This converts dates without regard to timezone. +%% Unixtime just goes to UTC +raw_to_date(Unixtime) when is_integer(Unixtime) -> + unixtime_to_date(Unixtime); +raw_to_date(DateString) when is_list(DateString) -> + ec_date:parse(DateString, get_deterministic_datetime()); +raw_to_date(Now = {_,_,_}) -> + calendar:now_to_datetime(Now); +raw_to_date(Date = {{_,_,_},{_,_,_}}) -> + Date. + +get_deterministic_datetime() -> + DateZero = {1970,1,1}, + TimeZero = {0,0,0}, + case application:get_env(qdate, deterministic_parsing) of + {ok, {zero, zero}} -> {DateZero, TimeZero}; + {ok, {zero, now}} -> {DateZero, time()}; + {ok, {now, zero}} -> {date(), TimeZero}; + {ok, {now, now}} -> {date(), time()}; + undefined -> {DateZero, TimeZero}; + {ok, Val} -> throw({invalid_env_var, {qdate, deterministic_parsing, Val}}) + end. + + +to_unixtime(Unixtime) when is_integer(Unixtime) -> + Unixtime; +to_unixtime({MegaSecs,Secs,_}) -> + MegaSecs*1000000 + Secs; +to_unixtime(ToParse) -> + %% We want to treat all unixtimes as GMT + Date = to_date("GMT", ToParse), + calendar:datetime_to_gregorian_seconds(Date) - ?UNIXTIME_BASE. + +unixtime() -> + to_unixtime(os:timestamp()). + +to_now(Now = {_,_,_}) -> + Now; +to_now(ToParse) -> + Unixtime = to_unixtime(ToParse), + unixtime_to_now(Unixtime). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Comparisons %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +compare(A, B) -> + NowA = to_now(A), + NowB = to_now(B), + if + NowA == NowB -> 0; + NowA < NowB -> -1; + NowA > NowB -> 1 + end. + +compare(A, Op, B) -> + Comp = compare(A, B), + case Op of + '==' -> Comp =:= 0; + '=' -> Comp =:= 0; + + '!=' -> Comp =/= 0; + '=/=' -> Comp =/= 0; + '/=' -> Comp =/= 0; + + '<' -> Comp =:= -1; + '<=' -> Comp =:= -1 orelse Comp =:= 0; + '=<' -> Comp =:= -1 orelse Comp =:= 0; + + '>' -> Comp =:= 1; + '>=' -> Comp =:= 1 orelse Comp =:= 0; + '=>' -> Comp =:= 1 orelse Comp =:= 0 + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Timezone Stuff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +get_timezone_shift(TZ, Date) -> + case localtime:tz_shift(Date, TZ) of + unable_to_detect -> {error,unable_to_detect}; + {error,T} -> {error,T}; + {Sh, _DstSh} -> Sh; + Sh -> Sh + end. + extract_timezone(Unixtime) when is_integer(Unixtime) -> {Unixtime, "GMT"}; @@ -248,29 +333,6 @@ determine_timezone() -> TZ -> TZ end. -%% This converts dates without regard to timezone. -%% Unixtime just goes to UTC -raw_to_date(Unixtime) when is_integer(Unixtime) -> - unixtime_to_date(Unixtime); -raw_to_date(DateString) when is_list(DateString) -> - ec_date:parse(DateString, get_deterministic_datetime()); -raw_to_date(Now = {_,_,_}) -> - calendar:now_to_datetime(Now); -raw_to_date(Date = {{_,_,_},{_,_,_}}) -> - Date. - -get_deterministic_datetime() -> - DateZero = {1970,1,1}, - TimeZero = {0,0,0}, - case application:get_env(qdate, deterministic_parsing) of - {ok, {zero, zero}} -> {DateZero, TimeZero}; - {ok, {zero, now}} -> {DateZero, time()}; - {ok, {now, zero}} -> {date(), TimeZero}; - {ok, {now, now}} -> {date(), time()}; - undefined -> {DateZero, TimeZero}; - {ok, Val} -> throw({invalid_env_var, {qdate, deterministic_parsing, Val}}) - end. - %% If FromTZ is an integer, then it's an integer that represents the number of minutes %% relative to GMT. So we convert the date to GMT based on that number, then we can %% do the other timezone conversion. @@ -281,6 +343,51 @@ date_tz_to_tz(Date, FromTZ, ToTZ) -> ActualToTZ = ensure_timezone(ToTZ), localtime:local_to_local(Date,FromTZ,ActualToTZ). +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). + +ensure_timezone(Key) when is_atom(Key) orelse is_tuple(Key) -> + case get_timezone(Key) of + undefined -> throw({timezone_key_not_found,Key}); + ToTZ -> ToTZ + end; +ensure_timezone(TZ) when is_binary(TZ) -> + binary_to_list(TZ); +ensure_timezone(TZ) when is_list(TZ) -> + TZ. + +clear_timezone() -> + qdate_srv:clear_timezone(). + +clear_timezone(Key) -> + qdate_srv:clear_timezone(Key). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Register Parsers %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +register_parser(Key, Parser) when is_function(Parser,1) -> + qdate_srv:register_parser(Key,Parser). + +register_parser(Parser) when is_function(Parser,1) -> + qdate_srv:register_parser(Parser). + +deregister_parser(Key) -> + qdate_srv:deregister_parser(Key). + +deregister_parsers() -> + qdate_srv:deregister_parsers(). + try_registered_parsers(RawDate) -> Parsers = qdate_srv:get_parsers(), try_parsers(RawDate,Parsers). @@ -302,67 +409,9 @@ try_parsers(RawDate,[{ParserKey,Parser}|Parsers]) -> throw({error_in_parser,[{error,{Error,Reason}},{parser_key,ParserKey}]}) end. -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). - - -ensure_timezone(Key) when is_atom(Key) orelse is_tuple(Key) -> - case get_timezone(Key) of - undefined -> throw({timezone_key_not_found,Key}); - ToTZ -> ToTZ - end; -ensure_timezone(TZ) when is_binary(TZ) -> - binary_to_list(TZ); -ensure_timezone(TZ) when is_list(TZ) -> - TZ. - - -clear_timezone() -> - qdate_srv:clear_timezone(). - -clear_timezone(Key) -> - qdate_srv:clear_timezone(Key). - - -to_unixtime(Unixtime) when is_integer(Unixtime) -> - Unixtime; -to_unixtime({MegaSecs,Secs,_}) -> - MegaSecs*1000000 + Secs; -to_unixtime(ToParse) -> - %% We want to treat all unixtimes as GMT - Date = to_date("GMT", ToParse), - calendar:datetime_to_gregorian_seconds(Date) - ?UNIXTIME_BASE. - -unixtime() -> - to_unixtime(os:timestamp()). - -to_now(Now = {_,_,_}) -> - Now; -to_now(ToParse) -> - Unixtime = to_unixtime(ToParse), - unixtime_to_now(Unixtime). - - -register_parser(Key, Parser) when is_function(Parser,1) -> - qdate_srv:register_parser(Key,Parser). - -register_parser(Parser) when is_function(Parser,1) -> - qdate_srv:register_parser(Parser). - -deregister_parser(Key) -> - qdate_srv:deregister_parser(Key). - -deregister_parsers() -> - qdate_srv:deregister_parsers(). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Register Formats %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% register_format(Key, Format) -> qdate_srv:register_format(Key, Format). @@ -371,6 +420,7 @@ deregister_format(Key) -> qdate_srv:deregister_format(Key). + unixtime_to_now(T) when is_integer(T) -> MegaSec = floor(T/1000000), Secs = T - MegaSec*1000000, @@ -389,8 +439,10 @@ floor(N) when N < 0 -> true -> Int-1 end. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% TESTS %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% TESTS -include_lib("eunit/include/eunit.hrl"). %% emulates as if a forum-type website has a Site tz, and a user-specified tz @@ -408,6 +460,7 @@ tz_test_() -> fun(SetupData) -> {inorder,[ simple_test(SetupData), + compare_test(SetupData), tz_tests(SetupData), test_process_die(SetupData), parser_format_test(SetupData), @@ -496,6 +549,23 @@ simple_test(_) -> ?_assertEqual({{2013,1,1},{0,15,15}},to_date("GMT", "December 31, 2012 6:15:15pm CST")) ]}. + +compare_test(_) -> + {inorder,[ + ?_assertEqual(true, compare({{2013,9,10},{0,0,0}},'=',"Sep 10th, 2013 12:00am")), + ?_assertEqual(true, compare("9/10/2013 1am EST",'==',"Sep 10th, 2013 12:00:00am CST")), + ?_assertEqual(true, compare({{2013,9,10},{0,0,0}},'=<',"Sep 10th, 2013 12:00am")), + ?_assertEqual(false, compare({{2013,9,10},{0,0,1}},'=',"Sep 10th, 2013 12:00am")), + ?_assertEqual(true, compare({{2013,9,10},{0,0,1}},'=/=',"Sep 10th, 2013 12:00am")), + ?_assertEqual(true, compare({{2013,9,10},{0,0,1}},'>',"Sep 10th, 2013 12:00am")), + ?_assertEqual(false, compare({{2013,9,10},{0,0,1}},'<',"Sep 10th, 2013 12:00am")), + ?_assertEqual(true, compare({{2013,9,10},{0,0,1}},'<',"Sep 10th, 2013 12:00:02am")), + ?_assertEqual(true, compare({{2013,9,10},{0,0,1}},'<',"Sep 10th, 2013 12:02am")), + ?_assertEqual(true, compare({{2013,9,10},{0,0,1}},'<',"Sep 10th, 2013 1am")), + ?_assertEqual(true, compare({{2013,9,9},{23,59,59}},'<',"Sep 10th, 2013 12am")), + ?_assertEqual(false, compare({{2013,9,9},{23,59,59}},'>',"Sep 10th, 2013 12am")) + ]}. + parser_format_test(_) -> {inorder,[ ?_assertEqual({{2008,2,8},{0,0,0}},to_date("20080208")),