Packages

Small Gleam helpers to make numbers, bytes, durations and lists human-friendly.

Current section

Files

Jump to
humanize src humanize.erl
Raw

src/humanize.erl

-module(humanize).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/humanize.gleam").
-export([number_with/3, number/1, number_with_opts/2, get_locale_data/1, make_locale/7, get_locale_data_with/2, time_ago_unix/5, time_ago_unix_with_overrides/6, time_ago_millis/5, time_ago_timestamp/5, time_ago_timestamp_with_overrides/6, time_ago_millis_with_overrides/6, bytes_decimal/1, bytes_binary/1, percent_with/4, percent/1, percent_ratio/4, duration/1, duration_precise/1, list_locale/3, list/1, list_with_oxford/1, list_with_ampersand/1, ordinal/1]).
-export_type([decimal_separator/0, number_opts/0, locale_data/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 decimal_separator() :: dot | comma.
-type number_opts() :: {number_opts, integer(), decimal_separator()}.
-type locale_data() :: {locale_data,
binary(),
binary(),
binary(),
binary(),
boolean(),
list({integer(), binary(), binary()})}.
-file("src/humanize.gleam", 20).
-spec decimal_sep_from_sep(decimal_separator()) -> binary().
decimal_sep_from_sep(Sep) ->
case Sep of
dot ->
<<"."/utf8>>;
comma ->
<<","/utf8>>
end.
-file("src/humanize.gleam", 28).
-spec pow10_acc(integer(), integer()) -> integer().
pow10_acc(N, Acc) ->
case N =< 0 of
true ->
Acc;
false ->
pow10_acc(N - 1, Acc * 10)
end.
-file("src/humanize.gleam", 35).
-spec pow10(integer()) -> integer().
pow10(N) ->
pow10_acc(N, 1).
-file("src/humanize.gleam", 168).
-spec pow_int_acc(integer(), integer(), integer()) -> integer().
pow_int_acc(Base, Exp, Acc) ->
case Exp =< 0 of
true ->
Acc;
false ->
pow_int_acc(Base, Exp - 1, Acc * Base)
end.
-file("src/humanize.gleam", 175).
-spec pow_int(integer(), integer()) -> integer().
pow_int(Base, Exp) ->
pow_int_acc(Base, Exp, 1).
-file("src/humanize.gleam", 183).
-spec join_strings(list(binary()), binary()) -> binary().
join_strings(Xs, Sep) ->
case Xs of
[] ->
<<""/utf8>>;
[A] ->
A;
[A@1 | Rest] ->
gleam@list:fold(
Rest,
A@1,
fun(Acc, X) ->
<<<<Acc/binary, Sep/binary>>/binary, X/binary>>
end
)
end.
-file("src/humanize.gleam", 179).
-spec repeat_string(integer(), binary()) -> binary().
repeat_string(N, S) ->
join_strings(gleam@list:repeat(S, N), <<""/utf8>>).
-file("src/humanize.gleam", 46).
?DOC(" Format an integer using a specific number of decimals and decimal separator.\n").
-spec number_with(integer(), integer(), decimal_separator()) -> binary().
number_with(N, Decimals, Sep) ->
Abs = case N < 0 of
true ->
- N;
false ->
N
end,
Sign = case N < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
Format_scaled = fun(N@1, Denom, Suffix, Decimals@1, Sep@1, Sign@1) ->
Mul = pow10(Decimals@1),
Scaled = case Denom of
0 -> 0;
Gleam@denominator -> ((N@1 * Mul) + (Denom div 2)) div Gleam@denominator
end,
Whole = case Mul of
0 -> 0;
Gleam@denominator@1 -> Scaled div Gleam@denominator@1
end,
Frac = case Mul of
0 -> 0;
Gleam@denominator@2 -> Scaled rem Gleam@denominator@2
end,
case Decimals@1 =:= 0 of
true ->
<<<<Sign@1/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
Suffix/binary>>;
false ->
Frac_s = erlang:integer_to_binary(Frac),
Frac_len = string:length(Frac_s),
Frac_padded = case Frac_len < Decimals@1 of
true ->
Needed = Decimals@1 - Frac_len,
Zeros = repeat_string(Needed, <<"0"/utf8>>),
<<Zeros/binary, Frac_s/binary>>;
false ->
Frac_s
end,
<<<<<<<<Sign@1/binary,
(erlang:integer_to_binary(Whole))/binary>>/binary,
(decimal_sep_from_sep(Sep@1))/binary>>/binary,
Frac_padded/binary>>/binary,
Suffix/binary>>
end
end,
case Abs >= 1000000000000 of
true ->
Format_scaled(Abs, 1000000000000, <<"T"/utf8>>, Decimals, Sep, Sign);
false ->
case Abs >= 1000000000 of
true ->
Mul@1 = pow10(Decimals),
Scaled@1 = ((Abs * Mul@1) + (1000000000 div 2)) div 1000000000,
case Scaled@1 >= (1000 * Mul@1) of
true ->
Format_scaled(
Abs,
1000000000000,
<<"T"/utf8>>,
Decimals,
Sep,
Sign
);
false ->
Format_scaled(
Abs,
1000000000,
<<"B"/utf8>>,
Decimals,
Sep,
Sign
)
end;
false ->
case Abs >= 1000000 of
true ->
Mul@2 = pow10(Decimals),
Scaled@2 = ((Abs * Mul@2) + (1000000 div 2)) div 1000000,
case Scaled@2 >= (1000 * Mul@2) of
true ->
Format_scaled(
Abs,
1000000000,
<<"B"/utf8>>,
Decimals,
Sep,
Sign
);
false ->
Format_scaled(
Abs,
1000000,
<<"M"/utf8>>,
Decimals,
Sep,
Sign
)
end;
false ->
case Abs >= 1000 of
true ->
Mul@3 = pow10(Decimals),
Scaled@3 = ((Abs * Mul@3) + (1000 div 2))
div 1000,
case Scaled@3 >= (1000 * Mul@3) of
true ->
Format_scaled(
Abs,
1000000,
<<"M"/utf8>>,
Decimals,
Sep,
Sign
);
false ->
Format_scaled(
Abs,
1000,
<<"K"/utf8>>,
Decimals,
Sep,
Sign
)
end;
false ->
erlang:integer_to_binary(N)
end
end
end
end.
-file("src/humanize.gleam", 41).
?DOC(
" Format an integer into a compact human-friendly string.\n"
" Example: `1234` -> \"1.2K\".\n"
).
-spec number(integer()) -> binary().
number(N) ->
number_with(N, 1, dot).
-file("src/humanize.gleam", 139).
?DOC(" Format an integer using a `NumberOpts` record.\n").
-spec number_with_opts(integer(), number_opts()) -> binary().
number_with_opts(N, Opts) ->
case Opts of
{number_opts, Decimals, Sep} ->
number_with(N, Decimals, Sep)
end.
-file("src/humanize.gleam", 195).
-spec join_with_conj(list(binary()), binary()) -> binary().
join_with_conj(Items, Conj_full) ->
case Items of
[] ->
<<""/utf8>>;
[A] ->
A;
[A@1, B] ->
<<<<A@1/binary, Conj_full/binary>>/binary, B/binary>>;
_ ->
Reversed = lists:reverse(Items),
case Reversed of
[] ->
<<""/utf8>>;
[Last | Init_rev] ->
Init = lists:reverse(Init_rev),
<<<<(join_strings(Init, <<", "/utf8>>))/binary,
Conj_full/binary>>/binary,
Last/binary>>
end
end.
-file("src/humanize.gleam", 252).
-spec raw_to_locale_data(
{binary(),
binary(),
binary(),
binary(),
boolean(),
list({integer(), binary(), binary()})}
) -> locale_data().
raw_to_locale_data(Raw) ->
case Raw of
{Ago, In_, Nowv, Conj, Ago_prefix, Units} ->
{locale_data, Ago, In_, Nowv, Conj, Ago_prefix, Units}
end.
-file("src/humanize.gleam", 261).
-spec find_locale_raw(
binary(),
list({binary(),
{binary(),
binary(),
binary(),
binary(),
boolean(),
list({integer(), binary(), binary()})}})
) -> locale_data().
find_locale_raw(Locale, List) ->
case List of
[] ->
raw_to_locale_data(internals@locales:default_raw());
[{K, Raw} | Rest] ->
case K =:= Locale of
true ->
raw_to_locale_data(Raw);
false ->
find_locale_raw(Locale, Rest)
end
end.
-file("src/humanize.gleam", 282).
?DOC(" Retrieve the built-in `LocaleData` for a given locale code.\n").
-spec get_locale_data(binary()) -> locale_data().
get_locale_data(Locale) ->
find_locale_raw(Locale, internals@locales:locales_raw()).
-file("src/humanize.gleam", 291).
?DOC(
" Create a custom `LocaleData` entry.\n"
"\n"
" When `ago_prefix` is `True`, the \"ago\" word is placed **before** the\n"
" time core (e.g. FR \"il y a 2 heures\"). When `False` it goes **after**\n"
" (e.g. EN \"1 hour ago\").\n"
).
-spec make_locale(
binary(),
binary(),
binary(),
binary(),
binary(),
boolean(),
list({integer(), binary(), binary()})
) -> {binary(), locale_data()}.
make_locale(Code, Ago, In_, Nowv, Conj, Ago_prefix, Units) ->
{Code, {locale_data, Ago, In_, Nowv, Conj, Ago_prefix, Units}}.
-file("src/humanize.gleam", 304).
?DOC(" Lookup `LocaleData` with custom overrides.\n").
-spec get_locale_data_with(binary(), list({binary(), locale_data()})) -> locale_data().
get_locale_data_with(Locale, Overrides) ->
case Overrides of
[] ->
find_locale_raw(Locale, internals@locales:locales_raw());
[{K, D} | Rest] ->
case K =:= Locale of
true ->
D;
false ->
get_locale_data_with(Locale, Rest)
end
end.
-file("src/humanize.gleam", 320).
-spec format_unit(integer(), binary(), binary()) -> binary().
format_unit(Count, Singular, Plural) ->
case Count =:= 1 of
true ->
<<<<(erlang:integer_to_binary(Count))/binary, " "/utf8>>/binary,
Singular/binary>>;
false ->
<<<<(erlang:integer_to_binary(Count))/binary, " "/utf8>>/binary,
Plural/binary>>
end.
-file("src/humanize.gleam", 213).
-spec build_parts(integer(), list({integer(), binary(), binary()}), integer()) -> list(binary()).
build_parts(Rem, Us, Remaining) ->
case Remaining =< 0 of
true ->
[];
false ->
case Us of
[] ->
[];
[{Sec, Sing, Plur} | Rest] ->
case case Sec of
0 -> 0;
Gleam@denominator -> Rem div Gleam@denominator
end of
0 ->
build_parts(Rem, Rest, Remaining);
N ->
Part = format_unit(N, Sing, Plur),
New_rem = Rem - (N * Sec),
lists:append(
[Part],
build_parts(New_rem, Rest, Remaining - 1)
)
end
end
end.
-file("src/humanize.gleam", 328).
-spec time_ago_from_data(
locale_data(),
integer(),
integer(),
boolean(),
integer()
) -> binary().
time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units) ->
Delta = Now_seconds - Past_seconds,
Abs_delta = case Delta < 0 of
true ->
- Delta;
false ->
Delta
end,
Now_str = case Data of
{locale_data, _, _, Nowv, _, _, _} ->
Nowv
end,
case Abs_delta =< 5 of
true ->
Now_str;
false ->
Units = case Data of
{locale_data, _, _, _, _, _, U} ->
U
end,
Needed_units = case Precise of
true ->
Max_units;
false ->
1
end,
Parts = build_parts(Abs_delta, Units, Needed_units),
Core = join_with_conj(Parts, erlang:element(5, Data)),
case Delta < 0 of
true ->
<<<<(erlang:element(3, Data))/binary, " "/utf8>>/binary,
Core/binary>>;
false ->
case erlang:element(6, Data) of
true ->
<<<<(erlang:element(2, Data))/binary, " "/utf8>>/binary,
Core/binary>>;
false ->
<<<<Core/binary, " "/utf8>>/binary,
(erlang:element(2, Data))/binary>>
end
end
end.
-file("src/humanize.gleam", 376).
?DOC(" Format a Unix timestamp relative to `now_seconds` using built-in locales.\n").
-spec time_ago_unix(integer(), integer(), binary(), boolean(), integer()) -> binary().
time_ago_unix(Past_seconds, Now_seconds, Locale, Precise, Max_units) ->
Data = get_locale_data(Locale),
time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units).
-file("src/humanize.gleam", 388).
?DOC(" Like `time_ago_unix` but accepts a list of runtime overrides.\n").
-spec time_ago_unix_with_overrides(
integer(),
integer(),
binary(),
list({binary(), locale_data()}),
boolean(),
integer()
) -> binary().
time_ago_unix_with_overrides(
Past_seconds,
Now_seconds,
Locale,
Overrides,
Precise,
Max_units
) ->
Data = get_locale_data_with(Locale, Overrides),
time_ago_from_data(Data, Past_seconds, Now_seconds, Precise, Max_units).
-file("src/humanize.gleam", 401).
?DOC(" Convenience wrapper: accept timestamps in milliseconds instead of seconds.\n").
-spec time_ago_millis(integer(), integer(), binary(), boolean(), integer()) -> binary().
time_ago_millis(Past_millis, Now_millis, Locale, Precise, Max_units) ->
Past = Past_millis div 1000,
Now = Now_millis div 1000,
time_ago_unix(Past, Now, Locale, Precise, Max_units).
-file("src/humanize.gleam", 423).
?DOC(
" Format two `gleam_time` `Timestamp` values as a relative-time string.\n"
" This is the modern alternative to `time_ago_unix` when working with\n"
" `gleam_time/timestamp.system_time()` or parsed RFC-3339 timestamps.\n"
"\n"
" ```gleam\n"
" import gleam/time/timestamp\n"
"\n"
" humanize.time_ago_timestamp(past_ts, timestamp.system_time(), \"fr\", False, 1)\n"
" // -> \"il y a 2 heures\"\n"
" ```\n"
).
-spec time_ago_timestamp(
gleam@time@timestamp:timestamp(),
gleam@time@timestamp:timestamp(),
binary(),
boolean(),
integer()
) -> binary().
time_ago_timestamp(Past, Now, Locale, Precise, Max_units) ->
{Past_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Past),
{Now_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Now),
time_ago_unix(Past_s, Now_s, Locale, Precise, Max_units).
-file("src/humanize.gleam", 436).
?DOC(" Like `time_ago_timestamp` but accepts runtime locale overrides.\n").
-spec time_ago_timestamp_with_overrides(
gleam@time@timestamp:timestamp(),
gleam@time@timestamp:timestamp(),
binary(),
list({binary(), locale_data()}),
boolean(),
integer()
) -> binary().
time_ago_timestamp_with_overrides(
Past,
Now,
Locale,
Overrides,
Precise,
Max_units
) ->
{Past_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Past),
{Now_s, _} = gleam@time@timestamp:to_unix_seconds_and_nanoseconds(Now),
time_ago_unix_with_overrides(
Past_s,
Now_s,
Locale,
Overrides,
Precise,
Max_units
).
-file("src/humanize.gleam", 457).
?DOC(" Millisecond-based wrapper with overrides.\n").
-spec time_ago_millis_with_overrides(
integer(),
integer(),
binary(),
list({binary(), locale_data()}),
boolean(),
integer()
) -> binary().
time_ago_millis_with_overrides(
Past_millis,
Now_millis,
Locale,
Overrides,
Precise,
Max_units
) ->
Past = Past_millis div 1000,
Now = Now_millis div 1000,
time_ago_unix_with_overrides(
Past,
Now,
Locale,
Overrides,
Precise,
Max_units
).
-file("src/humanize.gleam", 470).
-spec find_denom_for(integer(), integer(), list(binary()), integer()) -> {integer(),
binary(),
list(binary())}.
find_denom_for(N, Base, Units, Idx) ->
case Units of
[] ->
{1, <<"B"/utf8>>, []};
[U] ->
{pow_int(Base, Idx), U, []};
[U@1 | Rest] ->
Denom = pow_int(Base, Idx),
Next = Denom * Base,
case N < Next of
true ->
{Denom, U@1, Rest};
false ->
find_denom_for(N, Base, Rest, Idx + 1)
end
end.
-file("src/humanize.gleam", 493).
-spec promote_if_needed_bytes(
integer(),
integer(),
integer(),
integer(),
binary(),
list(binary())
) -> {integer(), binary(), list(binary())}.
promote_if_needed_bytes(N, Base, Mul, Denom, Unit, Rest) ->
Scaled = case Denom of
0 -> 0;
Gleam@denominator -> ((N * Mul) + (Denom div 2)) div Gleam@denominator
end,
case Scaled >= (Base * Mul) of
true ->
case Rest of
[] ->
{Denom, Unit, Rest};
[Next | Rest2] ->
{Denom * Base, Next, Rest2}
end;
false ->
{Denom, Unit, Rest}
end.
-file("src/humanize.gleam", 513).
-spec bytes_with(
integer(),
integer(),
list(binary()),
integer(),
decimal_separator()
) -> binary().
bytes_with(Bytes, Base, Units, Decimals, Sep) ->
N = case Bytes < 0 of
true ->
- Bytes;
false ->
Bytes
end,
Sign = case Bytes < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
{Denom, Unit, Rest} = find_denom_for(N, Base, Units, 0),
Mul = pow10(Decimals),
{Denom@1, Unit@1, _} = promote_if_needed_bytes(
N,
Base,
Mul,
Denom,
Unit,
Rest
),
Scaled = case Denom@1 of
0 -> 0;
Gleam@denominator -> ((N * Mul) + (Denom@1 div 2)) div Gleam@denominator
end,
Whole = case Mul of
0 -> 0;
Gleam@denominator@1 -> Scaled div Gleam@denominator@1
end,
Frac = case Mul of
0 -> 0;
Gleam@denominator@2 -> Scaled rem Gleam@denominator@2
end,
case (Decimals =:= 0) orelse (Denom@1 =:= 1) of
true ->
<<<<<<Sign/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
" "/utf8>>/binary,
Unit@1/binary>>;
false ->
Frac_s = erlang:integer_to_binary(Frac),
Frac_len = string:length(Frac_s),
Frac_padded = case Frac_len < Decimals of
true ->
Needed = Decimals - Frac_len,
Zeros = repeat_string(Needed, <<"0"/utf8>>),
<<Zeros/binary, Frac_s/binary>>;
false ->
Frac_s
end,
<<<<<<<<<<Sign/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
(decimal_sep_from_sep(Sep))/binary>>/binary,
Frac_padded/binary>>/binary,
" "/utf8>>/binary,
Unit@1/binary>>
end.
-file("src/humanize.gleam", 146).
?DOC(" Format bytes using decimal units (base 1000), supports up to Yottabyte (YB).\n").
-spec bytes_decimal(integer()) -> binary().
bytes_decimal(Bytes) ->
bytes_with(
Bytes,
1000,
[<<"B"/utf8>>,
<<"KB"/utf8>>,
<<"MB"/utf8>>,
<<"GB"/utf8>>,
<<"TB"/utf8>>,
<<"PB"/utf8>>,
<<"EB"/utf8>>,
<<"ZB"/utf8>>,
<<"YB"/utf8>>],
1,
dot
).
-file("src/humanize.gleam", 157).
?DOC(" Format bytes using binary units (base 1024), supports up to Yobibyte (YiB).\n").
-spec bytes_binary(integer()) -> binary().
bytes_binary(Bytes) ->
bytes_with(
Bytes,
1024,
[<<"B"/utf8>>,
<<"KiB"/utf8>>,
<<"MiB"/utf8>>,
<<"GiB"/utf8>>,
<<"TiB"/utf8>>,
<<"PiB"/utf8>>,
<<"EiB"/utf8>>,
<<"ZiB"/utf8>>,
<<"YiB"/utf8>>],
1,
dot
).
-file("src/humanize.gleam", 573).
?DOC(" Format a percentage value with custom options.\n").
-spec percent_with(integer(), integer(), decimal_separator(), boolean()) -> binary().
percent_with(N, Decimals, Sep, Spaced) ->
Formatted = case Decimals =:= 0 of
true ->
erlang:integer_to_binary(N);
false ->
Zeros = repeat_string(Decimals, <<"0"/utf8>>),
<<<<(erlang:integer_to_binary(N))/binary,
(decimal_sep_from_sep(Sep))/binary>>/binary,
Zeros/binary>>
end,
case Spaced of
true ->
<<Formatted/binary, " %"/utf8>>;
false ->
<<Formatted/binary, "%"/utf8>>
end.
-file("src/humanize.gleam", 568).
?DOC(" Format an integer as a percentage string. Uses no decimals by default.\n").
-spec percent(integer()) -> binary().
percent(N) ->
percent_with(N, 0, dot, false).
-file("src/humanize.gleam", 595).
?DOC(" Compute and format a ratio as a percentage with rounding.\n").
-spec percent_ratio(integer(), integer(), integer(), decimal_separator()) -> binary().
percent_ratio(Numerator, Denominator, Decimals, Sep) ->
case Denominator =:= 0 of
true ->
<<"NaN%"/utf8>>;
false ->
Negative_num = Numerator < 0,
Negative_den = Denominator < 0,
Sign = case Negative_num /= Negative_den of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
N = case Numerator < 0 of
true ->
- Numerator;
false ->
Numerator
end,
D = case Denominator < 0 of
true ->
- Denominator;
false ->
Denominator
end,
Mul = pow10(Decimals),
Scaled = case D of
0 -> 0;
Gleam@denominator -> (((N * 100) * Mul) + (D div 2)) div Gleam@denominator
end,
Whole = case Mul of
0 -> 0;
Gleam@denominator@1 -> Scaled div Gleam@denominator@1
end,
Frac = case Mul of
0 -> 0;
Gleam@denominator@2 -> Scaled rem Gleam@denominator@2
end,
case Decimals =:= 0 of
true ->
<<<<Sign/binary, (erlang:integer_to_binary(Whole))/binary>>/binary,
"%"/utf8>>;
false ->
Frac_s = erlang:integer_to_binary(Frac),
Frac_len = string:length(Frac_s),
Frac_padded = case Frac_len < Decimals of
true ->
Needed = Decimals - Frac_len,
Zeros = repeat_string(Needed, <<"0"/utf8>>),
<<Zeros/binary, Frac_s/binary>>;
false ->
Frac_s
end,
<<<<<<<<Sign/binary,
(erlang:integer_to_binary(Whole))/binary>>/binary,
(decimal_sep_from_sep(Sep))/binary>>/binary,
Frac_padded/binary>>/binary,
"%"/utf8>>
end
end.
-file("src/humanize.gleam", 655).
?DOC(
" Format a duration in seconds into a single largest unit description.\n"
" Negative values are formatted with a leading \"-\".\n"
).
-spec duration(integer()) -> binary().
duration(Seconds) ->
Abs = case Seconds < 0 of
true ->
- Seconds;
false ->
Seconds
end,
Sign = case Seconds < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
Body = case Abs < 60 of
true ->
case Abs =:= 1 of
true ->
<<"1 second"/utf8>>;
false ->
<<(erlang:integer_to_binary(Abs))/binary, " seconds"/utf8>>
end;
false ->
case Abs < 3600 of
true ->
M = Abs div 60,
case M =:= 1 of
true ->
<<"1 minute"/utf8>>;
false ->
<<(erlang:integer_to_binary(M))/binary,
" minutes"/utf8>>
end;
false ->
case Abs < 86400 of
true ->
H = Abs div 3600,
case H =:= 1 of
true ->
<<"1 hour"/utf8>>;
false ->
<<(erlang:integer_to_binary(H))/binary,
" hours"/utf8>>
end;
false ->
D = Abs div 86400,
case D =:= 1 of
true ->
<<"1 day"/utf8>>;
false ->
<<(erlang:integer_to_binary(D))/binary,
" days"/utf8>>
end
end
end
end,
<<Sign/binary, Body/binary>>.
-file("src/humanize.gleam", 706).
?DOC(
" Precise duration with multiple units, joined by commas.\n"
" Negative values are formatted with a leading \"-\".\n"
).
-spec duration_precise(integer()) -> binary().
duration_precise(Seconds) ->
Abs = case Seconds < 0 of
true ->
- Seconds;
false ->
Seconds
end,
Sign = case Seconds < 0 of
true ->
<<"-"/utf8>>;
false ->
<<""/utf8>>
end,
D = Abs div 86400,
H = (Abs rem 86400) div 3600,
M = (Abs rem 3600) div 60,
S = Abs rem 60,
Parts = [],
Parts@1 = case D > 0 of
true ->
case D =:= 1 of
true ->
lists:append(Parts, [<<"1 day"/utf8>>]);
false ->
lists:append(
Parts,
[<<(erlang:integer_to_binary(D))/binary, " days"/utf8>>]
)
end;
false ->
Parts
end,
Parts@2 = case H > 0 of
true ->
case H =:= 1 of
true ->
lists:append(Parts@1, [<<"1 hour"/utf8>>]);
false ->
lists:append(
Parts@1,
[<<(erlang:integer_to_binary(H))/binary, " hours"/utf8>>]
)
end;
false ->
Parts@1
end,
Parts@3 = case M > 0 of
true ->
case M =:= 1 of
true ->
lists:append(Parts@2, [<<"1 minute"/utf8>>]);
false ->
lists:append(
Parts@2,
[<<(erlang:integer_to_binary(M))/binary,
" minutes"/utf8>>]
)
end;
false ->
Parts@2
end,
Parts@4 = case S > 0 of
true ->
case S =:= 1 of
true ->
lists:append(Parts@3, [<<"1 second"/utf8>>]);
false ->
lists:append(
Parts@3,
[<<(erlang:integer_to_binary(S))/binary,
" seconds"/utf8>>]
)
end;
false ->
Parts@3
end,
case Parts@4 of
[] ->
<<"0 seconds"/utf8>>;
_ ->
<<Sign/binary, (join_strings(Parts@4, <<", "/utf8>>))/binary>>
end.
-file("src/humanize.gleam", 779).
?DOC(" Join a list with a custom conjunction and Oxford comma option.\n").
-spec list_locale(list(binary()), binary(), boolean()) -> binary().
list_locale(Items, Conj, Oxford) ->
Resolved_conj = case Conj /= <<""/utf8>> of
true ->
Conj;
false ->
case Oxford of
true ->
<<", and "/utf8>>;
false ->
<<" and "/utf8>>
end
end,
Pair_conj = case Conj /= <<""/utf8>> of
true ->
Conj;
false ->
<<" and "/utf8>>
end,
case Items of
[] ->
<<""/utf8>>;
[A] ->
A;
[A@1, B] ->
<<<<A@1/binary, Pair_conj/binary>>/binary, B/binary>>;
_ ->
join_with_conj(Items, Resolved_conj)
end.
-file("src/humanize.gleam", 763).
?DOC(" Join a list of strings into a human-readable sentence fragment.\n").
-spec list(list(binary())) -> binary().
list(Items) ->
list_locale(Items, <<""/utf8>>, false).
-file("src/humanize.gleam", 768).
?DOC(" Join a list using the Oxford comma when appropriate.\n").
-spec list_with_oxford(list(binary())) -> binary().
list_with_oxford(Items) ->
list_locale(Items, <<""/utf8>>, true).
-file("src/humanize.gleam", 774).
?DOC(" Join a list using an ampersand as the final conjunction.\n").
-spec list_with_ampersand(list(binary())) -> binary().
list_with_ampersand(Items) ->
list_locale(Items, <<" & "/utf8>>, false).
-file("src/humanize.gleam", 802).
?DOC(" Return the English ordinal suffix for an integer (e.g., 1st, 2nd).\n").
-spec ordinal(integer()) -> binary().
ordinal(N) ->
Abs = case N < 0 of
true ->
- N;
false ->
N
end,
Mod100 = Abs rem 100,
Mod10 = Abs rem 10,
Suffix = case Mod100 of
11 ->
<<"th"/utf8>>;
12 ->
<<"th"/utf8>>;
13 ->
<<"th"/utf8>>;
_ ->
case Mod10 of
1 ->
<<"st"/utf8>>;
2 ->
<<"nd"/utf8>>;
3 ->
<<"rd"/utf8>>;
_ ->
<<"th"/utf8>>
end
end,
<<(erlang:integer_to_binary(N))/binary, Suffix/binary>>.