Skip to content

Instantly share code, notes, and snippets.

@johninvictus
Created December 22, 2020 13:02
Show Gist options
  • Save johninvictus/9bbfd9d233e80b92d6d91b1d74deda50 to your computer and use it in GitHub Desktop.
Save johninvictus/9bbfd9d233e80b92d6d91b1d74deda50 to your computer and use it in GitHub Desktop.
duration_parser.ex
defmodule DurationParser do
@moduledoc """
Parse a given string as either a time interval or a fractional number of hours
and return the equivalent number of hours and minutes.
## Examples
iex> DurationParser.parse_minutes("2:15")
{:ok, 135}
iex> DurationParser.parse_minutes("02:15")
{:ok, 135}
iex> DurationParser.parse_minutes("2h 35m")
{:ok, 155}
iex> DurationParser.parse_minutes("10")
{:ok, 10}
iex> DurationParser.parse_minutes("0.5h")
{:ok, 30}
iex> DurationParser.parse_minutes("0.5")
{:ok, 30}
iex> DurationParser.parse_minutes("10.0")
{:ok, 600}
iex> DurationParser.parse_minutes("7.5")
{:ok, 450}
iex> DurationParser.parse_minutes("24.5")
{:ok, 1470}
iex> DurationParser.parse_minutes("a24.5")
{:error, "expected 2 digits"}
"""
@type response :: {:ok, integer()} | :error
def parse_minutes(str) do
with :error <- parse_hour_colon_str(str),
:error <- parse_hours_space_minutes(str),
:error <- parse_fraction_hours(str),
:error <- parse_only_minutes(str) do
{:error, "expected 2 digits"}
end
end
@doc """
This function will be used to match duration with full colon in them
ie. "2:15
"""
@spec parse_hour_colon_str(String.t()) :: response
def parse_hour_colon_str(str) do
with [hours_str, minutes_str] <- try_parse_duration_colon(str),
{:ok, h_minutes} <- try_h_hours_to_minutes(hours_str),
{:ok, m_minutes} <- try_m_minutes_to_minutes(minutes_str) do
total = m_minutes + h_minutes
{:ok, total}
end
end
@doc """
This function will be for matching duration with a space in it
ie. 2h 35m
"""
@spec parse_hours_space_minutes(String.t()) :: response
def parse_hours_space_minutes(str) do
with [hours_str, minutes_str] <- try_parse_hours_space_minutes(str),
{:ok, h_minutes} <- try_h_hours_to_minutes(hours_str),
{:ok, m_minutes} <- try_m_minutes_to_minutes(minutes_str) do
total = m_minutes + h_minutes
{:ok, total}
end
end
@doc """
This function will be used for parsing fraction time
"""
@spec parse_fraction_hours(String.t()) :: response
def parse_fraction_hours(str) do
case String.split(str, ".") do
[hours_l, minutes_r] ->
minutes_str = String.replace(minutes_r, "h", "")
(hours_l <> "." <> minutes_str)
|> try_parse_as_number()
_ ->
:error
end
end
@doc ~S"""
This function will be used to parse minutes
It will accept a plain number or number with m in it ie
10 or 10m
"""
@spec parse_only_minutes(String.t()) :: response
def parse_only_minutes(str) do
try_m_minutes_to_minutes(str)
end
defp try_parse_hours_space_minutes(str) do
case String.split(str, " ") do
[hours_str, minutes_str] ->
[hours_str, minutes_str]
_ ->
:error
end
end
defp try_parse_duration_colon(str) do
case String.split(str, ":") do
[hours_str, minutes_str] ->
[hours_str, minutes_str]
_ ->
:error
end
end
defp try_h_hours_to_minutes(hours_str) do
case String.split(hours_str, "h") do
[hours, _] ->
try_hours_to_minutes(hours)
_ ->
try_hours_to_minutes(hours_str)
end
end
defp try_hours_to_minutes(hours_str) do
case Integer.parse(hours_str) do
{num, ""} ->
{:ok, num * 60}
_ ->
:error
end
end
# return minutes
defp try_m_minutes_to_minutes(minutes_str) do
case String.split(minutes_str, "m") do
[minutes, _] ->
try_minutes_to_minutes(minutes)
_ ->
try_minutes_to_minutes(minutes_str)
end
end
def try_minutes_to_minutes(minutes_str) do
case Integer.parse(minutes_str) do
{num, ""} ->
{:ok, num}
_ ->
:error
end
end
# work on fraction
defp try_parse_as_number(str) do
case Float.parse(str) do
{num, ""} -> {:ok, trunc(num * 60)}
_ -> :error
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment