Packages

A lightweight Gleam library for formatting timestamps as human-readable relative time strings (e.g., '5 minutes ago', 'in 2 hours') with support for multiple locales.

Current section

8 Versions

Jump to

Compare versions

5 files changed
+98 additions
-67 deletions
  @@ -1,5 +1,5 @@
1 1 name = "timeago"
2 - version = "1.0.0"
2 + version = "1.1.0"
3 3
4 4 # Fill out these fields if you intend to generate HTML documentation or publish
5 5 # your project to the Hex package manager.
  @@ -1,6 +1,6 @@
1 1 {<<"name">>, <<"timeago">>}.
2 2 {<<"app">>, <<"timeago">>}.
3 - {<<"version">>, <<"1.0.0">>}.
3 + {<<"version">>, <<"1.1.0">>}.
4 4 {<<"description">>, <<"A lightweight library for describing time differences in a human readable format (e.g., '1 minute ago')."/utf8>>}.
5 5 {<<"licenses">>, [<<"Apache-2.0">>]}.
6 6 {<<"build_tools">>, [<<"gleam">>]}.
  @@ -8,15 +8,15 @@
8 8 {<<"Repository">>, <<"https://github.com/ayoung19/timeago">>}
9 9 ]}.
10 10 {<<"requirements">>, [
11 - {<<"gleam_time">>, [
12 - {<<"app">>, <<"gleam_time">>},
13 - {<<"optional">>, false},
14 - {<<"requirement">>, <<">= 1.3.0 and < 2.0.0">>}
15 - ]},
16 11 {<<"gleam_stdlib">>, [
17 12 {<<"app">>, <<"gleam_stdlib">>},
18 13 {<<"optional">>, false},
19 14 {<<"requirement">>, <<">= 0.44.0 and < 2.0.0">>}
15 + ]},
16 + {<<"gleam_time">>, [
17 + {<<"app">>, <<"gleam_time">>},
18 + {<<"optional">>, false},
19 + {<<"requirement">>, <<">= 1.3.0 and < 2.0.0">>}
20 20 ]}
21 21 ]}.
22 22 {<<"files">>, [
  @@ -1,5 +1,5 @@
1 1 {application, timeago, [
2 - {vsn, "1.0.0"},
2 + {vsn, "1.1.0"},
3 3 {applications, [gleam_stdlib,
4 4 gleam_time]},
5 5 {description, "A lightweight library for describing time differences in a human readable format (e.g., '1 minute ago')."},
  @@ -3,6 +3,41 @@
3 3 -define(FILEPATH, "src/timeago.gleam").
4 4 -export([time_ago/3]).
5 5
6 + -file("src/timeago.gleam", 46).
7 + -spec unit_to_string(gleam@time@duration:unit()) -> binary().
8 + unit_to_string(Unit) ->
9 + case Unit of
10 + nanosecond ->
11 + <<"nanosecond"/utf8>>;
12 +
13 + microsecond ->
14 + <<"microsecond"/utf8>>;
15 +
16 + millisecond ->
17 + <<"millisecond"/utf8>>;
18 +
19 + second ->
20 + <<"second"/utf8>>;
21 +
22 + minute ->
23 + <<"minute"/utf8>>;
24 +
25 + hour ->
26 + <<"hour"/utf8>>;
27 +
28 + day ->
29 + <<"day"/utf8>>;
30 +
31 + week ->
32 + <<"week"/utf8>>;
33 +
34 + month ->
35 + <<"month"/utf8>>;
36 +
37 + year ->
38 + <<"year"/utf8>>
39 + end.
40 +
6 41 -file("src/timeago.gleam", 6).
7 42 -spec time_ago(
8 43 gleam@time@timestamp:timestamp(),
  @@ -11,61 +46,42 @@
11 46 ) -> binary().
12 47 time_ago(Timestamp, Maybe_now, _) ->
13 48 Now = gleam@option:unwrap(Maybe_now, gleam@time@timestamp:system_time()),
14 - {Seconds, Nanoseconds} = gleam@time@duration:to_seconds_and_nanoseconds(
49 + {Amount, Unit} = gleam@time@duration:approximate(
15 50 gleam@time@timestamp:difference(Timestamp, Now)
16 51 ),
17 - Diff = case (Seconds < 0) andalso (Nanoseconds > 0) of
18 - true ->
19 - Seconds + 1;
52 + case Unit of
53 + nanosecond ->
54 + <<"just now"/utf8>>;
20 55
21 - false ->
22 - Seconds
23 - end,
24 - case Diff of
25 - 0 ->
56 + microsecond ->
57 + <<"just now"/utf8>>;
58 +
59 + millisecond ->
26 60 <<"just now"/utf8>>;
27 61
28 62 _ ->
29 - Diff_magnitude = gleam@int:absolute_value(Diff),
30 - {Amount, Unit} = case Diff_magnitude of
31 - N when N < 60 ->
32 - {N, <<"second"/utf8>>};
33 -
34 - N@1 when N@1 < 3600 ->
35 - {N@1 div 60, <<"minute"/utf8>>};
36 -
37 - N@2 when N@2 < 86400 ->
38 - {N@2 div 3600, <<"hour"/utf8>>};
39 -
40 - N@3 when N@3 < 2635200 ->
41 - {N@3 div 86400, <<"day"/utf8>>};
42 -
43 - N@4 when N@4 < 31536000 ->
44 - {N@4 div 2635200, <<"month"/utf8>>};
45 -
46 - N@5 ->
47 - {N@5 div 31536000, <<"year"/utf8>>}
48 - end,
49 - Unit_suffix = case Amount of
63 + Unit_string = unit_to_string(Unit),
64 + Magnitude = gleam@int:absolute_value(Amount),
65 + Unit_string_suffix = case Magnitude of
50 66 1 ->
51 67 <<""/utf8>>;
52 68
53 69 _ ->
54 70 <<"s"/utf8>>
55 71 end,
56 - case Diff < 0 of
72 + case Amount < 0 of
57 73 true ->
58 74 <<<<<<<<"in "/utf8,
59 - (erlang:integer_to_binary(Amount))/binary>>/binary,
75 + (erlang:integer_to_binary(Magnitude))/binary>>/binary,
60 76 " "/utf8>>/binary,
61 - Unit/binary>>/binary,
62 - Unit_suffix/binary>>;
77 + Unit_string/binary>>/binary,
78 + Unit_string_suffix/binary>>;
63 79
64 80 false ->
65 - <<<<<<<<(erlang:integer_to_binary(Amount))/binary,
81 + <<<<<<<<(erlang:integer_to_binary(Magnitude))/binary,
66 82 " "/utf8>>/binary,
67 - Unit/binary>>/binary,
68 - Unit_suffix/binary>>/binary,
83 + Unit_string/binary>>/binary,
84 + Unit_string_suffix/binary>>/binary,
69 85 " ago"/utf8>>
70 86 end
71 87 end.
  @@ -9,36 +9,51 @@ pub fn time_ago(
9 9 _maybe_locale: Option(String),
10 10 ) -> String {
11 11 let now = option.unwrap(maybe_now, timestamp.system_time())
12 - let #(seconds, nanoseconds) =
13 - duration.to_seconds_and_nanoseconds(timestamp.difference(timestamp, now))
12 + let #(amount, unit) =
13 + duration.approximate(timestamp.difference(timestamp, now))
14 14
15 - let diff = case seconds < 0 && nanoseconds > 0 {
16 - True -> seconds + 1
17 - False -> seconds
18 - }
19 -
20 - case diff {
21 - 0 -> "just now"
15 + case unit {
16 + duration.Nanosecond | duration.Microsecond | duration.Millisecond ->
17 + "just now"
22 18 _ -> {
23 - let diff_magnitude = int.absolute_value(diff)
24 - let #(amount, unit) = case diff_magnitude {
25 - n if n < 60 -> #(n, "second")
26 - n if n < 3600 -> #(n / 60, "minute")
27 - n if n < 86_400 -> #(n / 3600, "hour")
28 - n if n < 2_635_200 -> #(n / 86_400, "day")
29 - n if n < 31_536_000 -> #(n / 2_635_200, "month")
30 - n -> #(n / 31_536_000, "year")
31 - }
19 + let unit_string = unit_to_string(unit)
32 20
33 - let unit_suffix = case amount {
21 + let magnitude = int.absolute_value(amount)
22 +
23 + let unit_string_suffix = case magnitude {
34 24 1 -> ""
35 25 _ -> "s"
36 26 }
37 27
38 - case diff < 0 {
39 - True -> "in " <> int.to_string(amount) <> " " <> unit <> unit_suffix
40 - False -> int.to_string(amount) <> " " <> unit <> unit_suffix <> " ago"
28 + case amount < 0 {
29 + True ->
30 + "in "
31 + <> int.to_string(magnitude)
32 + <> " "
33 + <> unit_string
34 + <> unit_string_suffix
35 + False ->
36 + int.to_string(magnitude)
37 + <> " "
38 + <> unit_string
39 + <> unit_string_suffix
40 + <> " ago"
41 41 }
42 42 }
43 43 }
44 44 }
45 +
46 + fn unit_to_string(unit: duration.Unit) -> String {
47 + case unit {
48 + duration.Nanosecond -> "nanosecond"
49 + duration.Microsecond -> "microsecond"
50 + duration.Millisecond -> "millisecond"
51 + duration.Second -> "second"
52 + duration.Minute -> "minute"
53 + duration.Hour -> "hour"
54 + duration.Day -> "day"
55 + duration.Week -> "week"
56 + duration.Month -> "month"
57 + duration.Year -> "year"
58 + }
59 + }