-module(birl@duration). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/birl/duration.gleam"). -export([add/2, subtract/2, scale_up/2, scale_down/2, micro_seconds/1, compare/2, to_gleam_duration/1, from_gleam_duration/1, milli_seconds/1, seconds/1, minutes/1, hours/1, days/1, weeks/1, months/1, years/1, new/1, decompose/1, blur_to/2, blur/1, accurate_new/1, accurate_decompose/1, parse/1]). -export_type([duration/0, unit/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type duration() :: {duration, integer()}. -type unit() :: micro_second | milli_second | second | minute | hour | day | week | month | year. -file("src/birl/duration.gleam", 26). -spec add(duration(), duration()) -> duration(). add(A, B) -> {duration, A@1} = A, {duration, B@1} = B, {duration, A@1 + B@1}. -file("src/birl/duration.gleam", 32). -spec subtract(duration(), duration()) -> duration(). subtract(A, B) -> {duration, A@1} = A, {duration, B@1} = B, {duration, A@1 - B@1}. -file("src/birl/duration.gleam", 38). -spec scale_up(duration(), integer()) -> duration(). scale_up(Value, Factor) -> {duration, Value@1} = Value, {duration, Value@1 * Factor}. -file("src/birl/duration.gleam", 43). -spec scale_down(duration(), integer()) -> duration(). scale_down(Value, Factor) -> {duration, Value@1} = Value, {duration, case Factor of 0 -> 0; Gleam@denominator -> Value@1 div Gleam@denominator end}. -file("src/birl/duration.gleam", 48). -spec micro_seconds(integer()) -> duration(). micro_seconds(Value) -> {duration, Value}. -file("src/birl/duration.gleam", 84). -spec compare(duration(), duration()) -> gleam@order:order(). compare(A, B) -> {duration, Dta} = A, {duration, Dtb} = B, case {Dta =:= Dtb, Dta < Dtb} of {true, _} -> eq; {_, true} -> lt; {_, false} -> gt end. -file("src/birl/duration.gleam", 391). -spec extract(integer(), integer()) -> {integer(), integer()}. extract(Duration, Unit_value) -> {case Unit_value of 0 -> 0; Gleam@denominator -> Duration div Gleam@denominator end, case Unit_value of 0 -> 0; Gleam@denominator@1 -> Duration rem Gleam@denominator@1 end}. -file("src/birl/duration.gleam", 402). ?DOC( " Convert birl Duration to gleam_time Duration.\n" "\n" " birl uses microseconds internally, while gleam_time uses nanoseconds.\n" ). -spec to_gleam_duration(duration()) -> gleam@time@duration:duration(). to_gleam_duration(D) -> {duration, Microseconds} = D, gleam@time@duration:nanoseconds(Microseconds * 1000). -file("src/birl/duration.gleam", 412). ?DOC( " Convert gleam_time Duration to birl Duration.\n" "\n" " gleam_time uses nanoseconds internally, while birl uses microseconds.\n" " Sub-microsecond precision will be lost.\n" ). -spec from_gleam_duration(gleam@time@duration:duration()) -> duration(). from_gleam_duration(D) -> {Seconds, Nanoseconds} = gleam@time@duration:to_seconds_and_nanoseconds(D), {duration, (Seconds * 1000000) + (Nanoseconds div 1000)}. -file("src/birl/duration.gleam", 52). -spec milli_seconds(integer()) -> duration(). milli_seconds(Value) -> {duration, Value * 1000}. -file("src/birl/duration.gleam", 56). -spec seconds(integer()) -> duration(). seconds(Value) -> {duration, Value * 1000000}. -file("src/birl/duration.gleam", 60). -spec minutes(integer()) -> duration(). minutes(Value) -> {duration, Value * 60000000}. -file("src/birl/duration.gleam", 64). -spec hours(integer()) -> duration(). hours(Value) -> {duration, Value * 3600000000}. -file("src/birl/duration.gleam", 68). -spec days(integer()) -> duration(). days(Value) -> {duration, Value * 86400000000}. -file("src/birl/duration.gleam", 72). -spec weeks(integer()) -> duration(). weeks(Value) -> {duration, Value * 604800000000}. -file("src/birl/duration.gleam", 76). -spec months(integer()) -> duration(). months(Value) -> {duration, Value * 2592000000000}. -file("src/birl/duration.gleam", 80). -spec years(integer()) -> duration(). years(Value) -> {duration, Value * 31536000000000}. -file("src/birl/duration.gleam", 96). ?DOC(" use this if you need short durations where a year just means 365 days and a month just means 30 days\n"). -spec new(list({integer(), unit()})) -> duration(). new(Values) -> _pipe = Values, _pipe@1 = gleam@list:fold(_pipe, 0, fun(Total, Current) -> case Current of {Amount, micro_second} -> Total + Amount; {Amount@1, milli_second} -> Total + (Amount@1 * 1000); {Amount@2, second} -> Total + (Amount@2 * 1000000); {Amount@3, minute} -> Total + (Amount@3 * 60000000); {Amount@4, hour} -> Total + (Amount@4 * 3600000000); {Amount@5, day} -> Total + (Amount@5 * 86400000000); {Amount@6, week} -> Total + (Amount@6 * 604800000000); {Amount@7, month} -> Total + (Amount@7 * 2592000000000); {Amount@8, year} -> Total + (Amount@8 * 31536000000000) end end), {duration, _pipe@1}. -file("src/birl/duration.gleam", 134). ?DOC(" use this if you need short durations where a year just means 365 days and a month just means 30 days\n"). -spec decompose(duration()) -> list({integer(), unit()}). decompose(Duration) -> {duration, Value} = Duration, Absolute_value = gleam@int:absolute_value(Value), {Years, Remaining} = extract(Absolute_value, 31536000000000), {Months, Remaining@1} = extract(Remaining, 2592000000000), {Weeks, Remaining@2} = extract(Remaining@1, 604800000000), {Days, Remaining@3} = extract(Remaining@2, 86400000000), {Hours, Remaining@4} = extract(Remaining@3, 3600000000), {Minutes, Remaining@5} = extract(Remaining@4, 60000000), {Seconds, Remaining@6} = extract(Remaining@5, 1000000), {Milli_seconds, Remaining@7} = extract(Remaining@6, 1000), _pipe = [{Years, year}, {Months, month}, {Weeks, week}, {Days, day}, {Hours, hour}, {Minutes, minute}, {Seconds, second}, {Milli_seconds, milli_second}, {Remaining@7, micro_second}], _pipe@1 = gleam@list:filter( _pipe, fun(Item) -> erlang:element(1, Item) > 0 end ), gleam@list:map(_pipe@1, fun(Item@1) -> case Value < 0 of true -> {-1 * erlang:element(1, Item@1), erlang:element(2, Item@1)}; false -> Item@1 end end). -file("src/birl/duration.gleam", 244). -spec unit_values(unit()) -> integer(). unit_values(Unit) -> case Unit of year -> 31536000000000; month -> 2592000000000; week -> 604800000000; day -> 86400000000; hour -> 3600000000; minute -> 60000000; second -> 1000000; milli_second -> 1000; micro_second -> 1 end. -file("src/birl/duration.gleam", 207). ?DOC( " approximates the duration by only the given unit\n" " \n" " if the duration is not an integer multiple of the unit,\n" " the remainder will be disgarded if it's less than two thirds of the unit,\n" " otherwise a single unit will be added to the multiplier.\n" " \n" " - `blur_to(days(16), Month)` -> `0`\n" " - `blur_to(days(20), Month)` -> `1`\n" ). -spec blur_to(duration(), unit()) -> integer(). blur_to(Duration, Unit) -> Unit_value = unit_values(Unit), {duration, Value} = Duration, {Unit_counts, Remaining} = extract(Value, Unit_value), case Remaining >= ((Unit_value * 2) div 3) of true -> Unit_counts + 1; false -> Unit_counts end. -file("src/birl/duration.gleam", 258). -spec inner_blur(list({integer(), unit()})) -> {integer(), unit()}. inner_blur(Values) -> case Values of [] -> {0, micro_second}; [Single] -> Single; [Smaller, Larger | Rest] -> Smaller_unit_value = unit_values(erlang:element(2, Smaller)), Larger_unit_value = unit_values(erlang:element(2, Larger)), At_least_two_thirds = (erlang:element(1, Smaller) * Smaller_unit_value) < ((Larger_unit_value * 2) div 3), Rounded = case At_least_two_thirds of true -> Larger; false -> {erlang:element(1, Larger) + 1, erlang:element(2, Larger)} end, inner_blur([Rounded | Rest]) end. -file("src/birl/duration.gleam", 218). ?DOC(" approximates the duration by a value in a single unit\n"). -spec blur(duration()) -> {integer(), unit()}. blur(Duration) -> _pipe = decompose(Duration), _pipe@1 = lists:reverse(_pipe), inner_blur(_pipe@1). -file("src/birl/duration.gleam", 115). ?DOC(" use this if you need very long durations where small inaccuracies could lead to large errors\n"). -spec accurate_new(list({integer(), unit()})) -> duration(). accurate_new(Values) -> _pipe = Values, _pipe@1 = gleam@list:fold(_pipe, 0, fun(Total, Current) -> case Current of {Amount, micro_second} -> Total + Amount; {Amount@1, milli_second} -> Total + (Amount@1 * 1000); {Amount@2, second} -> Total + (Amount@2 * 1000000); {Amount@3, minute} -> Total + (Amount@3 * 60000000); {Amount@4, hour} -> Total + (Amount@4 * 3600000000); {Amount@5, day} -> Total + (Amount@5 * 86400000000); {Amount@6, week} -> Total + (Amount@6 * 604800000000); {Amount@7, month} -> Total + (Amount@7 * 2629746000000); {Amount@8, year} -> Total + (Amount@8 * 31556952000000) end end), {duration, _pipe@1}. -file("src/birl/duration.gleam", 167). ?DOC(" use this if you need very long durations where small inaccuracies could lead to large errors\n"). -spec accurate_decompose(duration()) -> list({integer(), unit()}). accurate_decompose(Duration) -> {duration, Value} = Duration, Absolute_value = gleam@int:absolute_value(Value), {Years, Remaining} = extract(Absolute_value, 31556952000000), {Months, Remaining@1} = extract(Remaining, 2629746000000), {Weeks, Remaining@2} = extract(Remaining@1, 604800000000), {Days, Remaining@3} = extract(Remaining@2, 86400000000), {Hours, Remaining@4} = extract(Remaining@3, 3600000000), {Minutes, Remaining@5} = extract(Remaining@4, 60000000), {Seconds, Remaining@6} = extract(Remaining@5, 1000000), {Milli_seconds, Remaining@7} = extract(Remaining@6, 1000), _pipe = [{Years, year}, {Months, month}, {Weeks, week}, {Days, day}, {Hours, hour}, {Minutes, minute}, {Seconds, second}, {Milli_seconds, milli_second}, {Remaining@7, micro_second}], _pipe@1 = gleam@list:filter( _pipe, fun(Item) -> erlang:element(1, Item) > 0 end ), gleam@list:map(_pipe@1, fun(Item@1) -> case Value < 0 of true -> {-1 * erlang:element(1, Item@1), erlang:element(2, Item@1)}; false -> Item@1 end end). -file("src/birl/duration.gleam", 333). ?DOC( " you can use this function to create a new duration using expressions like:\n" "\n" " \"accurate: 1 Year - 2days + 152M -1h + 25 years + 25secs\"\n" "\n" " where the units are:\n" "\n" " Year: y, Y, YEAR, years, Years, ...\n" "\n" " Month: mon, Month, mONths, ...\n" "\n" " Week: w, W, Week, weeks, ...\n" "\n" " Day: d, D, day, Days, ...\n" "\n" " Hour: h, H, Hour, Hours, ...\n" "\n" " Minute: m, M, Min, minute, Minutes, ...\n" "\n" " Second: s, S, sec, Secs, second, Seconds, ...\n" "\n" " MilliSecond: ms, Msec, mSecs, milliSecond, MilliSecond, ...\n" "\n" " numbers with no unit are considered as microseconds.\n" " specifying `accurate:` is equivalent to using `accurate_new`.\n" ). -spec parse(binary()) -> {ok, duration()} | {error, nil}. parse(Expression) -> Re@1 = case gleam@regexp:from_string( <<"([+|\\-])?\\s*(\\d+)\\s*(\\w+)?"/utf8>> ) of {ok, Re} -> Re; _assert_fail -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"birl/duration"/utf8>>, function => <<"parse"/utf8>>, line => 334, value => _assert_fail, start => 8670, 'end' => 8743, pattern_start => 8681, pattern_end => 8687}) end, {Constructor, Expression@3} = case gleam_stdlib:string_starts_with( Expression, <<"accurate:"/utf8>> ) of true -> Expression@2 = case gleam@string:split(Expression, <<":"/utf8>>) of [_, Expression@1] -> Expression@1; _assert_fail@1 -> erlang:error(#{gleam_error => let_assert, message => <<"Pattern match failed, no pattern matched the value."/utf8>>, file => <>, module => <<"birl/duration"/utf8>>, function => <<"parse"/utf8>>, line => 340, value => _assert_fail@1, start => 8857, 'end' => 8915, pattern_start => 8868, pattern_end => 8883}) end, {fun accurate_new/1, Expression@2}; false -> {fun new/1, Expression} end, case begin _pipe = Expression@3, _pipe@1 = string:lowercase(_pipe), _pipe@2 = gleam@regexp:scan(Re@1, _pipe@1), gleam@list:try_map(_pipe@2, fun(Item) -> case Item of {match, _, [Sign_option, {some, Amount_string}]} -> gleam@result:'try'( gleam_stdlib:parse_int(Amount_string), fun(Amount) -> case Sign_option of {some, <<"-"/utf8>>} -> {ok, {-1 * Amount, micro_second}}; none -> {ok, {Amount, micro_second}}; {some, <<"+"/utf8>>} -> {ok, {Amount, micro_second}}; {some, _} -> {error, nil} end end ); {match, _, [Sign_option@1, {some, Amount_string@1}, {some, Unit}]} -> gleam@result:'try'( gleam_stdlib:parse_int(Amount_string@1), fun(Amount@1) -> gleam@result:'try'( gleam@list:find( [{year, [<<"y"/utf8>>, <<"year"/utf8>>, <<"years"/utf8>>]}, {month, [<<"mon"/utf8>>, <<"month"/utf8>>, <<"months"/utf8>>]}, {week, [<<"w"/utf8>>, <<"week"/utf8>>, <<"weeks"/utf8>>]}, {day, [<<"d"/utf8>>, <<"day"/utf8>>, <<"days"/utf8>>]}, {hour, [<<"h"/utf8>>, <<"hour"/utf8>>, <<"hours"/utf8>>]}, {minute, [<<"m"/utf8>>, <<"min"/utf8>>, <<"minute"/utf8>>, <<"minutes"/utf8>>]}, {second, [<<"s"/utf8>>, <<"sec"/utf8>>, <<"secs"/utf8>>, <<"second"/utf8>>, <<"seconds"/utf8>>]}, {milli_second, [<<"ms"/utf8>>, <<"msec"/utf8>>, <<"msecs"/utf8>>, <<"millisecond"/utf8>>, <<"milliseconds"/utf8>>, <<"milli-second"/utf8>>, <<"milli-seconds"/utf8>>, <<"milli_second"/utf8>>, <<"milli_seconds"/utf8>>]}], fun(Item@1) -> gleam@list:contains( erlang:element(2, Item@1), Unit ) end ), fun(_use0) -> {Unit@1, _} = _use0, case Sign_option@1 of {some, <<"-"/utf8>>} -> {ok, {-1 * Amount@1, Unit@1}}; none -> {ok, {Amount@1, Unit@1}}; {some, <<"+"/utf8>>} -> {ok, {Amount@1, Unit@1}}; {some, _} -> {error, nil} end end ) end ); _ -> {error, nil} end end) end of {ok, Values} -> _pipe@3 = Values, _pipe@4 = Constructor(_pipe@3), {ok, _pipe@4}; {error, nil} -> {error, nil} end.