Packages

An Erlang date/time processing library

Current section

Files

Jump to
jam src jam_math.erl
Raw

src/jam_math.erl

%% -------------------------------------------------------------------
%%
%% jam_math: Simple date/time math functions against Erlang's
%% date/time tuples
%%
%% Copyright (c) 2016 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
%%
%% -------------------------------------------------------------------
-module(jam_math).
-export([add_time/2, add_date/2, wrap/3]).
-ifdef(TEST).
-compile(export_all).
-include_lib("eunit/include/eunit.hrl").
-endif.
add_time({Hour, Minute, Second}, {AddH}) ->
add_time({Hour, Minute, Second}, {AddH, 0, 0});
add_time({Hour, Minute, Second}, {AddH, AddM}) ->
add_time({Hour, Minute, Second}, {AddH, AddM, 0});
add_time(Time, Diff) ->
to_tuple(
wrap_diff(tuple_to_list(Time), tuple_to_list(Diff),
[24, 60, 60])).
add_date(Date, NumDays) ->
calendar:gregorian_days_to_date(
calendar:date_to_gregorian_days(Date)+NumDays).
to_tuple([H|T]) ->
{H, list_to_tuple(T)}.
wrap_diff(Time, Diff, Wraps) ->
wrap_diff_reversed(lists:reverse(Time),
lists:reverse(Diff),
lists:reverse(Wraps), 0, []).
wrap_diff_reversed([], [], [], Carry, Acc) ->
[Carry|Acc];
wrap_diff_reversed([Hv|Tv], [Hd|Td], [Hw|Tw], Carry, Acc) ->
{NewCarry, NewVal} = wrap(Hv + Hd + Carry, Hw, 0),
wrap_diff_reversed(Tv, Td, Tw, NewCarry, [NewVal|Acc]).
%% Max is exclusive, Min is inclusive. Return value is a tuple:
%% `{Carry, Sum}'
wrap(Sum, Max, Min) ->
wrap(Sum, Max-Min, Max-1, Min).
%% Internal only. Max is now inclusive. Algorithm modified from
%% http://stackoverflow.com/a/707426
wrap(Sum, Range, Max, Min) when Sum < Min ->
NewSum = Sum + (Range * ((Min - Sum) div Range + 1)),
{_, Sum2} = wrap(NewSum, Range, Max, Min),
{(Sum+1) div Range - 1, Sum2};
wrap(Sum, Range, _Max, Min) ->
{Sum div Range, Min + ((Sum - Min) rem Range)}.
-ifdef(TEST).
negative_wrap_test() ->
?assertEqual({-1, 0}, wrap(-60, 60, 0)).
negative_wrap_nonzero_test() ->
?assertEqual({-1, 11}, wrap(-1, 13, 1)).
zero_wrap_nonzero_test() ->
?assertEqual({-1, 12}, wrap(0, 13, 1)).
add_time_test_() ->
Times = [
{
0, {20, 15, 45}, {1}, {21, 15, 45}
},
{
1, {20, 15, 45}, {4}, {0, 15, 45}
},
{
2, {20, 15, 45}, {28}, {0, 15, 45}
},
{
2, {20, 15, 45}, {28, 40}, {0, 55, 45}
},
{
2, {20, 15, 45}, {28, 40, 5}, {0, 55, 50}
},
{
2, {20, 15, 45}, {27, 100, 5}, {0, 55, 50}
},
{
-1, {2, 15, 45}, {-4, -15}, {22, 0, 45}
},
{
-2, {2, 15, 45}, {-28, -15}, {22, 0, 45}
},
{
-2, {2, 15, 45}, {-27, -75}, {22, 0, 45}
}
],
lists:map(fun({Adj, Old, Add, New}) ->
?_assertEqual({Adj, New}, add_time(Old, Add))
end, Times).
-endif.