stellar_prune/build/dev/javascript/gleam_time/gleam@time@duration.erl
2025-11-30 15:44:22 +01:00

381 lines
12 KiB
Erlang

-module(gleam@time@duration).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gleam/time/duration.gleam").
-export([approximate/1, compare/2, difference/2, add/2, seconds/1, minutes/1, hours/1, milliseconds/1, nanoseconds/1, to_seconds/1, to_seconds_and_nanoseconds/1, to_iso8601_string/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.
-opaque duration() :: {duration, integer(), integer()}.
-type unit() :: nanosecond |
microsecond |
millisecond |
second |
minute |
hour |
day |
week |
month |
year.
-file("src/gleam/time/duration.gleam", 110).
?DOC(
" Ensure the duration is represented with `nanoseconds` being positive and\n"
" less than 1 second.\n"
"\n"
" This function does not change the amount of time that the duratoin refers\n"
" to, it only adjusts the values used to represent the time.\n"
).
-spec normalise(duration()) -> duration().
normalise(Duration) ->
Multiplier = 1000000000,
Nanoseconds = case Multiplier of
0 -> 0;
Gleam@denominator -> erlang:element(3, Duration) rem Gleam@denominator
end,
Overflow = erlang:element(3, Duration) - Nanoseconds,
Seconds = erlang:element(2, Duration) + (case Multiplier of
0 -> 0;
Gleam@denominator@1 -> Overflow div Gleam@denominator@1
end),
case Nanoseconds >= 0 of
true ->
{duration, Seconds, Nanoseconds};
false ->
{duration, Seconds - 1, Multiplier + Nanoseconds}
end.
-file("src/gleam/time/duration.gleam", 76).
?DOC(
" Convert a duration to a number of the largest number of a unit, serving as\n"
" a rough description of the duration that a human can understand.\n"
"\n"
" The size used for each unit are described in the documentation for the\n"
" `Unit` type.\n"
"\n"
" ```gleam\n"
" seconds(125)\n"
" |> approximate\n"
" // -> #(2, Minute)\n"
" ```\n"
"\n"
" This function rounds _towards zero_. This means that if a duration is just\n"
" short of 2 days then it will approximate to 1 day.\n"
"\n"
" ```gleam\n"
" hours(47)\n"
" |> approximate\n"
" // -> #(1, Day)\n"
" ```\n"
).
-spec approximate(duration()) -> {integer(), unit()}.
approximate(Duration) ->
{duration, S, Ns} = Duration,
Minute = 60,
Hour = Minute * 60,
Day = Hour * 24,
Week = Day * 7,
Year = (Day * 365) + (Hour * 6),
Month = Year div 12,
Microsecond = 1000,
Millisecond = Microsecond * 1000,
case nil of
_ when S < 0 ->
{Amount, Unit} = begin
_pipe = {duration, - S, - Ns},
_pipe@1 = normalise(_pipe),
approximate(_pipe@1)
end,
{- Amount, Unit};
_ when S >= Year ->
{case Year of
0 -> 0;
Gleam@denominator -> S div Gleam@denominator
end, year};
_ when S >= Month ->
{case Month of
0 -> 0;
Gleam@denominator@1 -> S div Gleam@denominator@1
end, month};
_ when S >= Week ->
{case Week of
0 -> 0;
Gleam@denominator@2 -> S div Gleam@denominator@2
end, week};
_ when S >= Day ->
{case Day of
0 -> 0;
Gleam@denominator@3 -> S div Gleam@denominator@3
end, day};
_ when S >= Hour ->
{case Hour of
0 -> 0;
Gleam@denominator@4 -> S div Gleam@denominator@4
end, hour};
_ when S >= Minute ->
{case Minute of
0 -> 0;
Gleam@denominator@5 -> S div Gleam@denominator@5
end, minute};
_ when S > 0 ->
{S, second};
_ when Ns >= Millisecond ->
{case Millisecond of
0 -> 0;
Gleam@denominator@6 -> Ns div Gleam@denominator@6
end, millisecond};
_ when Ns >= Microsecond ->
{case Microsecond of
0 -> 0;
Gleam@denominator@7 -> Ns div Gleam@denominator@7
end, microsecond};
_ ->
{Ns, nanosecond}
end.
-file("src/gleam/time/duration.gleam", 140).
?DOC(
" Compare one duration to another, indicating whether the first spans a\n"
" larger amount of time (and so is greater) or smaller amount of time (and so\n"
" is lesser) than the second.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" compare(seconds(1), seconds(2))\n"
" // -> order.Lt\n"
" ```\n"
"\n"
" Whether a duration is negative or positive doesn't matter for comparing\n"
" them, only the amount of time spanned matters.\n"
"\n"
" ```gleam\n"
" compare(seconds(-2), seconds(1))\n"
" // -> order.Gt\n"
" ```\n"
).
-spec compare(duration(), duration()) -> gleam@order:order().
compare(Left, Right) ->
Parts = fun(X) -> case erlang:element(2, X) >= 0 of
true ->
{erlang:element(2, X), erlang:element(3, X)};
false ->
{(erlang:element(2, X) * -1) - 1,
1000000000 - erlang:element(3, X)}
end end,
{Ls, Lns} = Parts(Left),
{Rs, Rns} = Parts(Right),
_pipe = gleam@int:compare(Ls, Rs),
gleam@order:break_tie(_pipe, gleam@int:compare(Lns, Rns)).
-file("src/gleam/time/duration.gleam", 164).
?DOC(
" Calculate the difference between two durations.\n"
"\n"
" This is effectively substracting the first duration from the second.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" difference(seconds(1), seconds(5))\n"
" // -> seconds(4)\n"
" ```\n"
).
-spec difference(duration(), duration()) -> duration().
difference(Left, Right) ->
_pipe = {duration,
erlang:element(2, Right) - erlang:element(2, Left),
erlang:element(3, Right) - erlang:element(3, Left)},
normalise(_pipe).
-file("src/gleam/time/duration.gleam", 178).
?DOC(
" Add two durations together.\n"
"\n"
" # Examples\n"
"\n"
" ```gleam\n"
" add(seconds(1), seconds(5))\n"
" // -> seconds(6)\n"
" ```\n"
).
-spec add(duration(), duration()) -> duration().
add(Left, Right) ->
_pipe = {duration,
erlang:element(2, Left) + erlang:element(2, Right),
erlang:element(3, Left) + erlang:element(3, Right)},
normalise(_pipe).
-file("src/gleam/time/duration.gleam", 225).
-spec nanosecond_digits(integer(), integer(), binary()) -> binary().
nanosecond_digits(N, Position, Acc) ->
case Position of
9 ->
Acc;
_ when (Acc =:= <<""/utf8>>) andalso ((N rem 10) =:= 0) ->
nanosecond_digits(N div 10, Position + 1, Acc);
_ ->
Acc@1 = <<(erlang:integer_to_binary(N rem 10))/binary, Acc/binary>>,
nanosecond_digits(N div 10, Position + 1, Acc@1)
end.
-file("src/gleam/time/duration.gleam", 239).
?DOC(" Create a duration of a number of seconds.\n").
-spec seconds(integer()) -> duration().
seconds(Amount) ->
{duration, Amount, 0}.
-file("src/gleam/time/duration.gleam", 244).
?DOC(" Create a duration of a number of minutes.\n").
-spec minutes(integer()) -> duration().
minutes(Amount) ->
seconds(Amount * 60).
-file("src/gleam/time/duration.gleam", 249).
?DOC(" Create a duration of a number of hours.\n").
-spec hours(integer()) -> duration().
hours(Amount) ->
seconds((Amount * 60) * 60).
-file("src/gleam/time/duration.gleam", 254).
?DOC(" Create a duration of a number of milliseconds.\n").
-spec milliseconds(integer()) -> duration().
milliseconds(Amount) ->
Remainder = Amount rem 1000,
Overflow = Amount - Remainder,
Nanoseconds = Remainder * 1000000,
Seconds = Overflow div 1000,
_pipe = {duration, Seconds, Nanoseconds},
normalise(_pipe).
-file("src/gleam/time/duration.gleam", 273).
?DOC(
" Create a duration of a number of nanoseconds.\n"
"\n"
" # JavaScript int limitations\n"
"\n"
" Remember that JavaScript can only perfectly represent ints between positive\n"
" and negative 9,007,199,254,740,991! If you use a single call to this\n"
" function to create durations larger than that number of nanoseconds then\n"
" you will likely not get exactly the value you expect. Use `seconds` and\n"
" `milliseconds` as much as possible for large durations.\n"
).
-spec nanoseconds(integer()) -> duration().
nanoseconds(Amount) ->
_pipe = {duration, 0, Amount},
normalise(_pipe).
-file("src/gleam/time/duration.gleam", 283).
?DOC(
" Convert the duration to a number of seconds.\n"
"\n"
" There may be some small loss of precision due to `Duration` being\n"
" nanosecond accurate and `Float` not being able to represent this.\n"
).
-spec to_seconds(duration()) -> float().
to_seconds(Duration) ->
Seconds = erlang:float(erlang:element(2, Duration)),
Nanoseconds = erlang:float(erlang:element(3, Duration)),
Seconds + (Nanoseconds / 1000000000.0).
-file("src/gleam/time/duration.gleam", 292).
?DOC(
" Convert the duration to a number of seconds and nanoseconds. There is no\n"
" loss of precision with this conversion on any target.\n"
).
-spec to_seconds_and_nanoseconds(duration()) -> {integer(), integer()}.
to_seconds_and_nanoseconds(Duration) ->
{erlang:element(2, Duration), erlang:element(3, Duration)}.
-file("src/gleam/time/duration.gleam", 192).
?DOC(
" Convert the duration to an [ISO8601][1] formatted duration string.\n"
"\n"
" The ISO8601 duration format is ambiguous without context due to months and\n"
" years having different lengths, and because of leap seconds. This function\n"
" encodes the duration as days, hours, and seconds without any leap seconds.\n"
" Be sure to take this into account when using the duration strings.\n"
"\n"
" [1]: https://en.wikipedia.org/wiki/ISO_8601#Durations\n"
).
-spec to_iso8601_string(duration()) -> binary().
to_iso8601_string(Duration) ->
gleam@bool:guard(
Duration =:= {duration, 0, 0},
<<"PT0S"/utf8>>,
fun() ->
Split = fun(Total, Limit) ->
Amount = case Limit of
0 -> 0;
Gleam@denominator -> Total rem Gleam@denominator
end,
Remainder = case Limit of
0 -> 0;
Gleam@denominator@1 -> (Total - Amount) div Gleam@denominator@1
end,
{Amount, Remainder}
end,
{Seconds, Rest} = Split(erlang:element(2, Duration), 60),
{Minutes, Rest@1} = Split(Rest, 60),
{Hours, Rest@2} = Split(Rest@1, 24),
Days = Rest@2,
Add = fun(Out, Value, Unit) -> case Value of
0 ->
Out;
_ ->
<<<<Out/binary,
(erlang:integer_to_binary(Value))/binary>>/binary,
Unit/binary>>
end end,
Output = begin
_pipe = <<"P"/utf8>>,
_pipe@1 = Add(_pipe, Days, <<"D"/utf8>>),
_pipe@2 = gleam@string:append(_pipe@1, <<"T"/utf8>>),
_pipe@3 = Add(_pipe@2, Hours, <<"H"/utf8>>),
Add(_pipe@3, Minutes, <<"M"/utf8>>)
end,
case {Seconds, erlang:element(3, Duration)} of
{0, 0} ->
Output;
{_, 0} ->
<<<<Output/binary,
(erlang:integer_to_binary(Seconds))/binary>>/binary,
"S"/utf8>>;
{_, _} ->
F = nanosecond_digits(
erlang:element(3, Duration),
0,
<<""/utf8>>
),
<<<<<<<<Output/binary,
(erlang:integer_to_binary(Seconds))/binary>>/binary,
"."/utf8>>/binary,
F/binary>>/binary,
"S"/utf8>>
end
end
).