Skip to content

Commit

Permalink
Merge pull request #661 from Goju-Ryu/unit_list-based-human-function
Browse files Browse the repository at this point in the history
Unit list based human function
  • Loading branch information
sharkdp authored Dec 27, 2024
2 parents c7f568b + 6910817 commit 6fce77f
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 22 deletions.
5 changes: 4 additions & 1 deletion examples/tests/human.nbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ assert_eq((60.1 seconds -> human), "1 minute + 0.1 seconds")
assert_eq((1 minute -> human), "1 minute")
assert_eq((1.25 minute -> human), "1 minute + 15 seconds")
assert_eq((2.5 minute -> human), "2 minutes + 30 seconds")
assert_eq(((2 min + 59.99999 s )-> human), "3 minutes")

assert_eq((1 hour -> human), "1 hour")
assert_eq((1.5 hour -> human), "1 hour + 30 minutes")
assert_eq((2 hour -> human), "2 hours")
assert_eq((1 hour + 1 sec -> human), "1 hour + 1 second")
assert_eq((123 min -> human), "2 hours + 3 minutes")
assert_eq((1234 min -> human), "20 hours + 34 minutes")

assert_eq((1 day -> human), "1 day")
assert_eq((1.37 day -> human), "1 day + 8 hours + 52 minutes + 48 seconds")
Expand All @@ -41,7 +44,7 @@ assert_eq((1 ms -> human), "0.001 seconds")
assert_eq((1 µs -> human), "0.000001 seconds")
assert_eq((1 ns -> human), "1.0e-9 seconds")
assert_eq((1234 ns -> human), "0.000001234 seconds")
assert_eq((1s + 1234 ns -> human), "1 second")
assert_eq((1 s + 1234 ns -> human), "1 second")

assert_eq((-1 second -> human), "1 second ago")
assert_eq((-7.89 hour -> human), "7 hours + 53 minutes + 24 seconds ago")
2 changes: 1 addition & 1 deletion numbat/modules/core/mixed_units.nbt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn _mixed_unit_list<D: Dim>(val: D, units: List<D>, acc: List<D>) -> List<D> =
else _mixed_unit_list(val - unit_val, tail(units), cons_end(unit_val, acc))
where unit_val: D =
if (len(units) > 0)
then (val |> trunc_in(head(units)))
then ((val -> head(units)) |> trunc_in(head(units)))
else error("Units list cannot be empty")

fn _negate<D: Dim>(x: D) = -x
Expand Down
49 changes: 29 additions & 20 deletions numbat/modules/datetime/human.nbt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use core::functions
use core::lists
use core::strings
use core::quantities
use units::si
use units::time
use datetime::functions
use units::mixed

fn _human_join(a: String, b: String) -> String =
if a == "" then b else if b == "" then a else "{a} + {b}"
Expand All @@ -24,36 +27,42 @@ fn _precise_human_months(time: Time) -> String = "{(time -> months) / month }
fn _precise_human_days(time: Time) -> String = "{(time -> days) / day } days" -> _prettier
fn _precise_human_seconds(time: Time) -> String = "{(time -> seconds) / second} seconds" -> _prettier

fn _human_recurse(t: Time, result: String, time_unit: String) -> String =
if time_unit == "day"
then _human_recurse((t -> day) - (t |> floor_in(day)), _human_join(result, t -> _human_days), "hour")
else if time_unit == "hour"
then _human_recurse((t -> hour) - (t |> floor_in(hour)), _human_join(result, t -> _human_hours), "minute")
else if time_unit == "minute"
then _human_recurse((t -> min) - (t |> floor_in(min)), _human_join(result, t -> _human_minutes), "second")
else _human_join(result, (t |> round_in(ms)) -> _precise_human_seconds)
fn _human_unit(time: Time) -> String =
if time_unit >= year then _human_years(time)
else if time_unit >= month then _human_months(time)
else if time_unit >= day then _human_days(time)
else if time_unit >= hour then _human_hours(time)
else if time_unit >= minute then _human_minutes(time)
else if time != 0 s then _precise_human_seconds(time |> round_in(ms))
else ""
where time_unit = if (time == 0) then 0 s else unit_of(time)

fn _year_month_approx(t: Time) -> String = _human_join(the_years -> _human_years, t - the_years -> _human_months)
where the_years = t |> floor_in(year)
fn _round_mixed_in<D: Dim>(base: D, value: List<D>) -> List<D> =
value |> sum |> round_in(base) |> _unit_list(units)
where units: List<D> = value |> filter(is_nonzero) |> map(unit_of)

fn _human_manage_past(str: String, time: Time) = str_append(str, if time < 0 s then " ago" else "")
fn _human_time(base: Time, time_segments: List<Time>) -> String =
time_segments |> _round_mixed_in(base) |> map(_human_unit) |> foldl(_human_join, "")

fn _human_for_long_duration(human_days: String, human_years: String) -> String =
"{human_days} (approx. {human_years})"

fn _abs_human(time: Time) -> String =
if time == 0 s then "0 seconds"
else if time < 60 seconds then time -> _precise_human_seconds
else if time < 2 months then _human_recurse(time, "", "day")
else if time < 1 year
then _human_for_long_duration(time -> _precise_human_days, (time |> round_in(month/10)) -> _precise_human_months)
else if time < 100 years
then _human_for_long_duration(time -> _precise_human_days, _year_month_approx(time))
if abs_time == 0 seconds then "0 seconds"
else if abs_time < 60 seconds then abs_time -> _precise_human_seconds
else if abs_time < 2 months then ((abs_time -> seconds) |> unit_list([day, hour, minute, second]) |> _human_time(0.1 ms))
else if abs_time < 1 years then _human_for_long_duration(abs_time -> _precise_human_days, (abs_time |> round_in(month/10)) -> _precise_human_months)
else if abs_time < 100 years
then _human_for_long_duration(abs_time -> _precise_human_days, ((abs_time -> months) |> unit_list([year, month]) |> _human_time(month/10)))
else
_human_for_long_duration(time -> _precise_human_days, time -> _human_years)
_human_for_long_duration(abs_time -> _precise_human_days, abs_time -> _human_years)
where abs_time: Time = abs(time)

@name("Human-readable time duration")
@url("https://numbat.dev/doc/date-and-time.html")
@description("Converts a time duration to a human-readable string in days, hours, minutes and seconds.")
@example("century/1e6 -> human", "How long is a microcentury?")
fn human(time: Time) -> String = _human_manage_past(abs(time) -> _abs_human, time)
fn human(time: Time) -> String =
if time < 0 s
then str_append(_abs_human(time), " ago")
else _abs_human(time)

0 comments on commit 6fce77f

Please sign in to comment.