Current section
Files
Jump to
Current section
Files
src/timeago.erl
-module(timeago).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/timeago.gleam").
-export([with_now/2, with_locale/2, format/2, en_us/3, new/0, fr/3, pt_br/3]).
-export_type([time_ago/0, tense/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.
?MODULEDOC(
" A library for formatting timestamps as human-readable relative time strings.\n"
" \n"
" This library provides a simple way to convert timestamps into relative time\n"
" expressions like \"5 minutes ago\" or \"in 2 hours\". It supports multiple\n"
" locales and allows customization of the reference time.\n"
" \n"
" ## Basic Usage\n"
" \n"
" ```gleam\n"
" import gleam/time/duration\n"
" import gleam/time/timestamp\n"
" import timeago\n"
" \n"
" // Format a timestamp from 5 minutes ago\n"
" let past = timestamp.add(timestamp.system_time(), duration.minutes(-5))\n"
" timeago.new() |> timeago.format(past)\n"
" // -> \"5 minutes ago\"\n"
" ```\n"
).
-opaque time_ago() :: {time_ago,
gleam@time@timestamp:timestamp(),
fun((tense(), gleam@time@duration:unit(), integer()) -> binary())}.
-type tense() :: past | future.
-file("src/timeago.gleam", 77).
?DOC(
" Sets a custom reference time for calculating relative differences.\n"
" \n"
" By default, TimeAgo uses the current system time when created. This allows\n"
" you to specify a different reference point for testing, calculating from\n"
" specific moments, or maintaining consistency across operations.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" import gleam/time/timestamp\n"
" import timeago\n"
" \n"
" let assert Ok(reference) = timestamp.parse_rfc3339(\"2024-01-01T12:00:00Z\")\n"
" let assert Ok(past) = timestamp.parse_rfc3339(\"2024-01-01T11:00:00Z\")\n"
" \n"
" timeago.new()\n"
" |> timeago.with_now(reference)\n"
" |> timeago.format(past)\n"
" // -> \"1 hour ago\"\n"
" ```\n"
).
-spec with_now(time_ago(), gleam@time@timestamp:timestamp()) -> time_ago().
with_now(Time_ago, Now) ->
_record = Time_ago,
{time_ago, Now, erlang:element(3, _record)}.
-file("src/timeago.gleam", 99).
?DOC(
" Sets a custom locale function for formatting output strings.\n"
" \n"
" The locale function determines how relative time is expressed in different\n"
" languages.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" import gleam/time/duration\n"
" import gleam/time/timestamp\n"
" import timeago\n"
" \n"
" // Using the built-in French locale\n"
" timeago.new()\n"
" |> timeago.with_locale(timeago.fr)\n"
" |> timeago.format(timestamp.add(timestamp.system_time(), duration.hours(-2)))\n"
" // -> \"il y a 2 heures\"\n"
" ```\n"
).
-spec with_locale(
time_ago(),
fun((tense(), gleam@time@duration:unit(), integer()) -> binary())
) -> time_ago().
with_locale(Time_ago, Locale) ->
_record = Time_ago,
{time_ago, erlang:element(2, _record), Locale}.
-file("src/timeago.gleam", 143).
?DOC(
" Formats a timestamp as a human-readable relative time string.\n"
" \n"
" Calculates the difference between the given timestamp and the reference time\n"
" (set via `with_now()` or defaulting to the current time), then formats it\n"
" using the configured locale.\n"
" \n"
" The output automatically adjusts for singular/plural forms and selects\n"
" appropriate time units based on the magnitude of the difference.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" import gleam/time/duration\n"
" import gleam/time/timestamp\n"
" import timeago\n"
" \n"
" let now = timestamp.system_time()\n"
" \n"
" // Past times\n"
" timeago.new()\n"
" |> timeago.format(timestamp.add(now, duration.seconds(-5)))\n"
" // -> \"5 seconds ago\"\n"
" \n"
" timeago.new()\n"
" |> timeago.format(timestamp.add(now, duration.minutes(-1)))\n"
" // -> \"1 minute ago\"\n"
" \n"
" // Future times\n"
" timeago.new()\n"
" |> timeago.format(timestamp.add(now, duration.hours(2)))\n"
" // -> \"in 2 hours\"\n"
" \n"
" timeago.new()\n"
" |> timeago.format(timestamp.add(now, duration.days(1)))\n"
" // -> \"in 1 day\"\n"
" \n"
" timeago.new()\n"
" |> timeago.format(timestamp.add(now, duration.milliseconds(-500)))\n"
" // -> \"just now\"\n"
" ```\n"
).
-spec format(time_ago(), gleam@time@timestamp:timestamp()) -> binary().
format(Time_ago, Timestamp) ->
{Amount, Unit} = gleam@time@duration:approximate(
gleam@time@timestamp:difference(Timestamp, erlang:element(2, Time_ago))
),
Magnitude = gleam@int:absolute_value(Amount),
Tense = case Amount > 0 of
true ->
past;
false ->
future
end,
(erlang:element(3, Time_ago))(Tense, Unit, Magnitude).
-file("src/timeago.gleam", 176).
-spec replace_percent_d_with_int(binary(), integer()) -> binary().
replace_percent_d_with_int(S, I) ->
gleam@string:replace(S, <<"%d"/utf8>>, erlang:integer_to_binary(I)).
-file("src/timeago.gleam", 180).
-spec en_us(tense(), gleam@time@duration:unit(), integer()) -> binary().
en_us(Tense, Unit, Amount) ->
case {Tense, Unit, Amount} of
{_, nanosecond, _} ->
<<"just now"/utf8>>;
{_, microsecond, _} ->
<<"just now"/utf8>>;
{_, millisecond, _} ->
<<"just now"/utf8>>;
{past, second, 1} ->
<<"1 second ago"/utf8>>;
{past, second, A} ->
_pipe = <<"%d seconds ago"/utf8>>,
replace_percent_d_with_int(_pipe, A);
{past, minute, 1} ->
<<"1 minute ago"/utf8>>;
{past, minute, A@1} ->
_pipe@1 = <<"%d minutes ago"/utf8>>,
replace_percent_d_with_int(_pipe@1, A@1);
{past, hour, 1} ->
<<"1 hour ago"/utf8>>;
{past, hour, A@2} ->
_pipe@2 = <<"%d hours ago"/utf8>>,
replace_percent_d_with_int(_pipe@2, A@2);
{past, day, 1} ->
<<"1 day ago"/utf8>>;
{past, day, A@3} ->
_pipe@3 = <<"%d days ago"/utf8>>,
replace_percent_d_with_int(_pipe@3, A@3);
{past, week, 1} ->
<<"1 week ago"/utf8>>;
{past, week, A@4} ->
_pipe@4 = <<"%d weeks ago"/utf8>>,
replace_percent_d_with_int(_pipe@4, A@4);
{past, month, 1} ->
<<"1 month ago"/utf8>>;
{past, month, A@5} ->
_pipe@5 = <<"%d months ago"/utf8>>,
replace_percent_d_with_int(_pipe@5, A@5);
{past, year, 1} ->
<<"1 year ago"/utf8>>;
{past, year, A@6} ->
_pipe@6 = <<"%d years ago"/utf8>>,
replace_percent_d_with_int(_pipe@6, A@6);
{future, second, 1} ->
<<"in 1 second"/utf8>>;
{future, second, A@7} ->
_pipe@7 = <<"in %d seconds"/utf8>>,
replace_percent_d_with_int(_pipe@7, A@7);
{future, minute, 1} ->
<<"in 1 minute"/utf8>>;
{future, minute, A@8} ->
_pipe@8 = <<"in %d minutes"/utf8>>,
replace_percent_d_with_int(_pipe@8, A@8);
{future, hour, 1} ->
<<"in 1 hour"/utf8>>;
{future, hour, A@9} ->
_pipe@9 = <<"in %d hours"/utf8>>,
replace_percent_d_with_int(_pipe@9, A@9);
{future, day, 1} ->
<<"in 1 day"/utf8>>;
{future, day, A@10} ->
_pipe@10 = <<"in %d days"/utf8>>,
replace_percent_d_with_int(_pipe@10, A@10);
{future, week, 1} ->
<<"in 1 week"/utf8>>;
{future, week, A@11} ->
_pipe@11 = <<"in %d weeks"/utf8>>,
replace_percent_d_with_int(_pipe@11, A@11);
{future, month, 1} ->
<<"in 1 month"/utf8>>;
{future, month, A@12} ->
_pipe@12 = <<"in %d months"/utf8>>,
replace_percent_d_with_int(_pipe@12, A@12);
{future, year, 1} ->
<<"in 1 year"/utf8>>;
{future, year, A@13} ->
_pipe@13 = <<"in %d years"/utf8>>,
replace_percent_d_with_int(_pipe@13, A@13)
end.
-file("src/timeago.gleam", 53).
?DOC(
" Creates a new TimeAgo formatter with default settings.\n"
" \n"
" Uses the current system time as the reference point and the English (US)\n"
" locale for formatting.\n"
" \n"
" ## Examples\n"
" \n"
" ```gleam\n"
" import gleam/time/duration\n"
" import gleam/time/timestamp\n"
" import timeago\n"
" \n"
" timeago.new()\n"
" |> timeago.format(timestamp.add(timestamp.system_time(), duration.minutes(-5)))\n"
" // -> \"5 minutes ago\"\n"
" ```\n"
).
-spec new() -> time_ago().
new() ->
{time_ago, gleam@time@timestamp:system_time(), fun en_us/3}.
-file("src/timeago.gleam", 214).
-spec fr(tense(), gleam@time@duration:unit(), integer()) -> binary().
fr(Tense, Unit, Amount) ->
case {Tense, Unit, Amount} of
{_, nanosecond, _} ->
<<"à l'instant"/utf8>>;
{_, microsecond, _} ->
<<"à l'instant"/utf8>>;
{_, millisecond, _} ->
<<"à l'instant"/utf8>>;
{past, second, 1} ->
<<"il y a 1 seconde"/utf8>>;
{past, second, A} ->
_pipe = <<"il y a %d secondes"/utf8>>,
replace_percent_d_with_int(_pipe, A);
{past, minute, 1} ->
<<"il y a 1 minute"/utf8>>;
{past, minute, A@1} ->
_pipe@1 = <<"il y a %d minutes"/utf8>>,
replace_percent_d_with_int(_pipe@1, A@1);
{past, hour, 1} ->
<<"il y a 1 heure"/utf8>>;
{past, hour, A@2} ->
_pipe@2 = <<"il y a %d heures"/utf8>>,
replace_percent_d_with_int(_pipe@2, A@2);
{past, day, 1} ->
<<"il y a 1 jour"/utf8>>;
{past, day, A@3} ->
_pipe@3 = <<"il y a %d jours"/utf8>>,
replace_percent_d_with_int(_pipe@3, A@3);
{past, week, 1} ->
<<"il y a 1 semaine"/utf8>>;
{past, week, A@4} ->
_pipe@4 = <<"il y a %d semaines"/utf8>>,
replace_percent_d_with_int(_pipe@4, A@4);
{past, month, 1} ->
<<"il y a 1 mois"/utf8>>;
{past, month, A@5} ->
_pipe@5 = <<"il y a %d mois"/utf8>>,
replace_percent_d_with_int(_pipe@5, A@5);
{past, year, 1} ->
<<"il y a 1 an"/utf8>>;
{past, year, A@6} ->
_pipe@6 = <<"il y a %d ans"/utf8>>,
replace_percent_d_with_int(_pipe@6, A@6);
{future, second, 1} ->
<<"dans 1 seconde"/utf8>>;
{future, second, A@7} ->
_pipe@7 = <<"dans %d secondes"/utf8>>,
replace_percent_d_with_int(_pipe@7, A@7);
{future, minute, 1} ->
<<"dans 1 minute"/utf8>>;
{future, minute, A@8} ->
_pipe@8 = <<"dans %d minutes"/utf8>>,
replace_percent_d_with_int(_pipe@8, A@8);
{future, hour, 1} ->
<<"dans 1 heure"/utf8>>;
{future, hour, A@9} ->
_pipe@9 = <<"dans %d heures"/utf8>>,
replace_percent_d_with_int(_pipe@9, A@9);
{future, day, 1} ->
<<"dans 1 jour"/utf8>>;
{future, day, A@10} ->
_pipe@10 = <<"dans %d jours"/utf8>>,
replace_percent_d_with_int(_pipe@10, A@10);
{future, week, 1} ->
<<"dans 1 semaine"/utf8>>;
{future, week, A@11} ->
_pipe@11 = <<"dans %d semaines"/utf8>>,
replace_percent_d_with_int(_pipe@11, A@11);
{future, month, 1} ->
<<"dans 1 mois"/utf8>>;
{future, month, A@12} ->
_pipe@12 = <<"dans %d mois"/utf8>>,
replace_percent_d_with_int(_pipe@12, A@12);
{future, year, 1} ->
<<"dans 1 an"/utf8>>;
{future, year, A@13} ->
_pipe@13 = <<"dans %d ans"/utf8>>,
replace_percent_d_with_int(_pipe@13, A@13)
end.
-file("src/timeago.gleam", 249).
?DOC(" Translations for Brazilian Portuguese.\n").
-spec pt_br(tense(), gleam@time@duration:unit(), integer()) -> binary().
pt_br(Tense, Unit, Amount) ->
case {Tense, Unit, Amount} of
{_, nanosecond, _} ->
<<"agora mesmo"/utf8>>;
{_, microsecond, _} ->
<<"agora mesmo"/utf8>>;
{_, millisecond, _} ->
<<"agora mesmo"/utf8>>;
{past, second, 1} ->
<<"1 segundo atrás"/utf8>>;
{past, second, A} ->
_pipe = <<"%d segundos atrás"/utf8>>,
replace_percent_d_with_int(_pipe, A);
{past, minute, 1} ->
<<"1 minuto atrás"/utf8>>;
{past, minute, A@1} ->
_pipe@1 = <<"%d minutos atrás"/utf8>>,
replace_percent_d_with_int(_pipe@1, A@1);
{past, hour, 1} ->
<<"1 hora atrás"/utf8>>;
{past, hour, A@2} ->
_pipe@2 = <<"%d horas atrás"/utf8>>,
replace_percent_d_with_int(_pipe@2, A@2);
{past, day, 1} ->
<<"1 dia atrás"/utf8>>;
{past, day, A@3} ->
_pipe@3 = <<"%d dias atrás"/utf8>>,
replace_percent_d_with_int(_pipe@3, A@3);
{past, week, 1} ->
<<"1 semana atrás"/utf8>>;
{past, week, A@4} ->
_pipe@4 = <<"%d semanas atrás"/utf8>>,
replace_percent_d_with_int(_pipe@4, A@4);
{past, month, 1} ->
<<"1 mês atrás"/utf8>>;
{past, month, A@5} ->
_pipe@5 = <<"%d meses atrás"/utf8>>,
replace_percent_d_with_int(_pipe@5, A@5);
{past, year, 1} ->
<<"1 ano atrás"/utf8>>;
{past, year, A@6} ->
_pipe@6 = <<"%d anos atrás"/utf8>>,
replace_percent_d_with_int(_pipe@6, A@6);
{future, second, 1} ->
<<"daqui a 1 segundo"/utf8>>;
{future, second, A@7} ->
_pipe@7 = <<"daqui a %d segundos"/utf8>>,
replace_percent_d_with_int(_pipe@7, A@7);
{future, minute, 1} ->
<<"daqui a 1 minuto"/utf8>>;
{future, minute, A@8} ->
_pipe@8 = <<"daqui a %d minutos"/utf8>>,
replace_percent_d_with_int(_pipe@8, A@8);
{future, hour, 1} ->
<<"daqui a 1 hora"/utf8>>;
{future, hour, A@9} ->
_pipe@9 = <<"daqui a %d horas"/utf8>>,
replace_percent_d_with_int(_pipe@9, A@9);
{future, day, 1} ->
<<"daqui a 1 dia"/utf8>>;
{future, day, A@10} ->
_pipe@10 = <<"daqui a %d dias"/utf8>>,
replace_percent_d_with_int(_pipe@10, A@10);
{future, week, 1} ->
<<"daqui a 1 semana"/utf8>>;
{future, week, A@11} ->
_pipe@11 = <<"daqui a %d semanas"/utf8>>,
replace_percent_d_with_int(_pipe@11, A@11);
{future, month, 1} ->
<<"daqui a 1 mês"/utf8>>;
{future, month, A@12} ->
_pipe@12 = <<"daqui a %d meses"/utf8>>,
replace_percent_d_with_int(_pipe@12, A@12);
{future, year, 1} ->
<<"daqui a 1 ano"/utf8>>;
{future, year, A@13} ->
_pipe@13 = <<"daqui a %d anos"/utf8>>,
replace_percent_d_with_int(_pipe@13, A@13)
end.