Current section
Files
Jump to
Current section
Files
lib/humanize_time.ex
defmodule HumanizeTime do
@moduledoc """
Module for converting seconds and milliseconds to human readable timestamps.
"""
@type duration_map :: %{
days: integer(),
hours: integer(),
minutes: integer(),
seconds: integer()
}
@doc """
Formatter for converting seconds to a human readable format
## Examples
iex> HumanizeTime.format_seconds(23487)
"6 hr 31 min"
"""
@spec format_seconds(integer() | float()) :: String.t()
def format_seconds(nil), do: ""
def format_seconds(seconds) when is_float(seconds) do
seconds
|> Float.round()
|> Kernel.trunc()
|> format_seconds()
end
def format_seconds(seconds) do
initial_acc = %{
days: 0,
hours: 0,
minutes: 0,
seconds: 0
}
calculate_times(initial_acc, seconds)
|> print_duration()
end
@spec calculate_times(duration_map(), integer()) :: duration_map()
defp calculate_times(time_tracker, 0), do: time_tracker
defp calculate_times(time_tracker, seconds) do
day_seconds = 86_400
hour_seconds = 3_600
minute_seconds = 60
cond do
seconds / day_seconds >= 1 ->
days = time_tracker.days + div(seconds, day_seconds)
remaining_seconds = seconds - days * day_seconds
calculate_times(%{time_tracker | days: days}, remaining_seconds)
seconds / hour_seconds >= 1 ->
hours = time_tracker.hours + div(seconds, hour_seconds)
remaining_seconds = seconds - hours * hour_seconds
calculate_times(%{time_tracker | hours: hours}, remaining_seconds)
seconds / minute_seconds >= 1 ->
minutes = time_tracker.minutes + div(seconds, minute_seconds)
remaining_seconds = seconds - minutes * minute_seconds
calculate_times(%{time_tracker | minutes: minutes}, remaining_seconds)
true ->
%{time_tracker | seconds: seconds}
end
end
@spec print_duration(duration_map()) :: String.t()
defp print_duration(duration) do
%{days: days, hours: hours, minutes: minutes, seconds: seconds} = duration
cond do
days > 0 ->
day_string = print_days(days)
hour_string = print_hours(hours, minutes)
String.trim("#{day_string} #{hour_string}")
hours > 0 ->
hour_string = print_hours(hours, 0)
minute_string = print_minutes(minutes, seconds)
String.trim("#{hour_string} #{minute_string}")
minutes > 0 ->
minute_string = print_minutes(minutes, 0)
seconds_string = print_seconds(seconds)
String.trim("#{minute_string} #{seconds_string}")
true ->
String.trim(print_seconds(seconds))
end
end
@spec print_days(integer()) :: String.t()
defp print_days(0), do: ""
defp print_days(1), do: "1 day"
defp print_days(duration), do: "#{duration} days"
@spec print_hours(integer(), integer()) :: String.t()
defp print_hours(0, _), do: ""
defp print_hours(duration, minutes) when minutes >= 30, do: "#{duration + 1} hr"
defp print_hours(duration, _), do: "#{duration} hr"
@spec print_minutes(integer(), integer()) :: String.t()
defp print_minutes(0, _), do: ""
defp print_minutes(duration, seconds) when seconds >= 30, do: "#{duration + 1} min"
defp print_minutes(duration, _), do: "#{duration} min"
@spec print_seconds(integer()) :: String.t()
defp print_seconds(0), do: ""
defp print_seconds(duration), do: "#{duration} sec"
end