nathanhornby/emojiclock-elixir

View on GitHub
lib/emojiclock.ex

Summary

Maintainability
Test Coverage
defmodule EmojiClock do
  @moduledoc """
  Returns a clock emoji for a given hour.
  """

  @type datetime :: %DateTime{calendar: module,
                              day: non_neg_integer,
                              hour: non_neg_integer,
                              microsecond: {non_neg_integer, non_neg_integer},
                              minute: non_neg_integer,
                              month: non_neg_integer,
                              second: non_neg_integer,
                              std_offset: integer,
                              time_zone: String.t,
                              utc_offset: integer,
                              year: non_neg_integer,
                              zone_abbr: String.t}

  @doc ~S"""
  Returns a clock emoji for the `local` hour. Accepts an optional `integer` offset.

  ## Examples

      iex> # If it's 18:37 you'll get "🕕"
      iex> clock = EmojiClock.now!
      iex> is_bitstring(clock)
      true
  """
  @spec now!(integer) :: String.t
  def now!(offset \\ 0) do
    formated_time = format_time(Timex.local)
    hour_with_offset = formated_time + offset
    emoji(hour_with_offset)
  end

  @doc ~S"""
  Returns a clock emoji for the `local` hour. Accepts an optional `integer` offset.

  ## Examples

      iex> # If it's 18:37 you'll get {:ok, "🕕"}
      iex> {:ok, clock} = EmojiClock.now
      iex> is_bitstring(clock)
      true

      iex> # If it's 18:37 you'll get {:ok, "🕓"}
      iex> {:ok, clock} = EmojiClock.now(-2)
      iex> is_bitstring(clock)
      true

  Invalid input returns an error:

      iex> EmojiClock.now("+8")
      {:error, :invalid_argument}
  """
  @spec now(integer) :: {:ok, String.t} | {:error, atom}
  def now(offset \\ 0)
  def now(offset) when is_integer(offset) do
    formated_time = format_time(Timex.local)
    hour_with_offset = formated_time + offset
    {:ok, emoji(hour_with_offset)}
  end

  def now(_input), do: {:error, :invalid_argument}

  @doc ~S"""
  Returns a clock emoji for a given `integer` hour between 0 and 12.

  ## Examples

      iex> EmojiClock.hour!(6)
      "🕕"

      iex> EmojiClock.hour!(12)
      "🕛"
  """
  @spec hour!(non_neg_integer) :: String.t
  def hour!(hour) when is_integer(hour) and hour >= 0 and hour <= 12 do
    emoji(hour)
  end

  @doc ~S"""
  Returns a clock emoji for a given `integer` hour between 0 and 12.

  ## Examples

      iex> EmojiClock.hour(6)
      {:ok, "🕕"}

      iex> EmojiClock.hour(12)
      {:ok, "🕛"}

  Invalid input returns an error:

      iex> EmojiClock.hour("2")
      {:error, :invalid_argument}

      iex> EmojiClock.hour(16)
      {:error, :invalid_argument}
  """
  @spec hour(non_neg_integer) :: {:ok, String.t} | {:error, atom}
  def hour(hour) when is_integer(hour) and hour >= 0 and hour <= 12 do
    {:ok, emoji(hour)}
  end

  def hour(_input), do: {:error, :invalid_argument}

  @doc ~S"""
  Returns a clock emoji for a given UNIX timestamp `integer`.

  ## Examples

      iex> EmojiClock.unix!(475359803)
      "🕗"

      iex> EmojiClock.unix!(1491517308)
      "🕙"

  """
  @spec unix!(pos_integer) :: String.t
  def unix!(timestamp) when is_integer(timestamp) do
    do_unix(timestamp)
  end

  @doc ~S"""
  Returns a clock emoji for a given UNIX timestamp `integer`.

  ## Examples

      iex> EmojiClock.unix(475359803)
      {:ok, "🕗"}

      iex> EmojiClock.unix(1491517308)
      {:ok, "🕙"}

  Invalid input returns an error:

      iex> EmojiClock.unix("4753598030")
      {:error, :invalid_argument}

      iex> EmojiClock.unix("clock pls")
      {:error, :invalid_argument}
  """
  @spec unix(pos_integer) :: {:ok, String.t} | {:error, atom}
  def unix(timestamp) when is_integer(timestamp) do
    {:ok, do_unix(timestamp)}
  end

  def unix(_input), do: {:error, :invalid_argument}

  @doc ~S"""
  Returns a clock emoji for a given ISO datetime `bitsring`.

  ## Examples

      iex> EmojiClock.iso!("2017-04-06T20:32:16+00:00")
      "🕗"

      iex> EmojiClock.iso!("1985-01-23T22:07:54Z")
      "🕙"
  """
  @spec iso!(String.t) :: String.t
  def iso!(datetime) when is_bitstring(datetime) do
    do_iso(datetime)
  end

  @doc ~S"""
  Returns a clock emoji for a given ISO datetime `bitsring`.

  ## Examples

      iex> EmojiClock.iso!("2017-04-06T20:32:16+00:00")
      "🕗"

      iex> EmojiClock.iso!("1985-01-23T22:07:54Z")
      "🕙"

  Invalid input returns an error:

      iex> EmojiClock.iso(49)
      {:error, :invalid_argument}

      iex> EmojiClock.iso(~N[2000-01-01 04:00:07.000000])
      {:error, :invalid_argument}
  """
  @spec iso(String.t) :: {:ok, String.t} | {:error, atom}
  def iso(datetime) when is_bitstring(datetime) do
    {:ok, do_iso(datetime)}
  end

  def iso(_input), do: {:error, :invalid_argument}

  @doc ~S"""
  Returns a clock emoji for a given Elixir `NaiveDateTime` struct.

  ## Examples

      iex> EmojiClock.naive!(~N[2000-01-01 04:00:07.000000])
      "🕓"

      iex> EmojiClock.naive!(~N[1985-01-23 20:30:42.657002])
      "🕗"
  """
  @spec naive!(struct) :: String.t
  def naive!(datetime) when is_map(datetime) do
    do_elixir_native_format(datetime)
  end

  @doc ~S"""
  Returns a clock emoji for a given Elixir `NaiveDateTime` struct.

  ## Examples

      iex> EmojiClock.naive(~N[2000-01-01 04:00:07.000000])
      {:ok, "🕓"}

      iex> EmojiClock.naive(~N[1985-01-23 20:30:42.657002])
      {:ok, "🕗"}

  Invalid input returns an error:

      iex> EmojiClock.naive(49)
      {:error, :invalid_argument}

      iex> EmojiClock.naive("clock pls")
      {:error, :invalid_argument}
  """
  @spec naive(struct) :: {:ok, String.t} | {:error, atom}
  def naive(datetime) when is_map(datetime) do
    {:ok, do_elixir_native_format(datetime)}
  end

  def naive(_input), do: {:error, :invalid_argument}

  @doc ~S"""
  Returns a clock emoji for a given Elixir `Time` struct.

  ## Examples

      iex> EmojiClock.time!(~T[14:32:07.052])
      "🕑"

      iex> EmojiClock.time!(~T[08:21:42])
      "🕗"
  """
  @spec time!(struct) :: String.t
  def time!(time) do
    do_elixir_native_format(time)
  end

  @doc ~S"""
  Returns a clock emoji for a given Elixir `Time` struct.

  ## Examples

      iex> EmojiClock.time(~T[14:32:07.052])
      {:ok, "🕑"}

      iex> EmojiClock.time(~T[08:21:42])
      {:ok, "🕗"}

  Invalid input returns an error:

      iex> EmojiClock.time(49)
      {:error, :invalid_argument}

      iex> EmojiClock.time("clock pls")
      {:error, :invalid_argument}
  """
  @spec time(struct) :: {:ok, String.t} | {:error, atom}
  def time(time) when is_map(time) do
    {:ok, do_elixir_native_format(time)}
  end

  def time(_input), do: {:error, :invalid_argument}

  @doc ~S"""
  Returns a clock emoji for a given Elixir `DateTime` struct.

  ## Examples

      iex> datetime = %DateTime{calendar: Calendar.ISO,
      ...>                      day: 7,
      ...>                      hour: 15,
      ...>                      microsecond: {817699, 6},
      ...>                      minute: 5,
      ...>                      month: 4,
      ...>                      second: 43,
      ...>                      std_offset: 0,
      ...>                      time_zone: "Etc/UTC",
      ...>                      utc_offset: 0,
      ...>                      year: 2017,
      ...>                      zone_abbr: "UTC"}
      iex> EmojiClock.datetime!(datetime)
      "🕒"

      iex> datetime = %DateTime{calendar: Calendar.ISO,
      ...>                      day: 3,
      ...>                      hour: 8,
      ...>                      microsecond: {000000, 6},
      ...>                      minute: 42,
      ...>                      month: 1,
      ...>                      second: 23,
      ...>                      std_offset: 0,
      ...>                      time_zone: "Etc/UTC",
      ...>                      utc_offset: 0,
      ...>                      year: 1985,
      ...>                      zone_abbr: "UTC"}
      iex> EmojiClock.datetime!(datetime)
      "🕗"
  """
  @spec datetime!(datetime) :: String.t
  def datetime!(datetime) do
    do_elixir_native_format(datetime)
  end

  @doc ~S"""
  Returns a clock emoji for a given Elixir `DateTime` struct.

  ## Examples

      iex> datetime = %DateTime{calendar: Calendar.ISO,
      ...>                      day: 7,
      ...>                      hour: 15,
      ...>                      microsecond: {817699, 6},
      ...>                      minute: 5,
      ...>                      month: 4,
      ...>                      second: 43,
      ...>                      std_offset: 0,
      ...>                      time_zone: "Etc/UTC",
      ...>                      utc_offset: 0,
      ...>                      year: 2017,
      ...>                      zone_abbr: "UTC"}
      iex> EmojiClock.datetime(datetime)
      {:ok, "🕒"}

      iex> datetime = %DateTime{calendar: Calendar.ISO,
      ...>                      day: 3,
      ...>                      hour: 8,
      ...>                      microsecond: {000000, 6},
      ...>                      minute: 42,
      ...>                      month: 1,
      ...>                      second: 23,
      ...>                      std_offset: 0,
      ...>                      time_zone: "Etc/UTC",
      ...>                      utc_offset: 0,
      ...>                      year: 1985,
      ...>                      zone_abbr: "UTC"}
      iex> EmojiClock.datetime(datetime)
      {:ok, "🕗"}

  Invalid input returns an error:

      iex> EmojiClock.datetime(49)
      {:error, :invalid_argument}

      iex> EmojiClock.datetime("clock pls")
      {:error, :invalid_argument}
  """
  @spec datetime(datetime) :: {:ok, String.t} | {:error, atom}
  def datetime(datetime) when is_map(datetime) do
    {:ok, do_elixir_native_format(datetime)}
  end

  def datetime(_input), do: {:error, :invalid_argument}

  # Private functions

  defp do_unix(timestamp) do
    timestamp
    |> Timex.from_unix
    |> format_time
    |> emoji
  end

  defp do_iso(datetime) do
    datetime
    |> Timex.parse!("{ISO:Extended}")
    |> format_time
    |> emoji
  end

  defp do_elixir_native_format(datetime) do
    datetime
    |> format_time
    |> emoji
  end

  defp format_time(input) do
    input
    |> Timex.format!("{h12}")
    |> String.to_integer
  end

  defp emoji(hour) do
    clocks = %{
               0 => "🕛",
               1 => "🕐",
               2 => "🕑",
               3 => "🕒",
               4 => "🕓",
               5 => "🕔",
               6 => "🕕",
               7 => "🕖",
               8 => "🕗",
               9 => "🕘",
              10 => "🕙",
              11 => "🕚",
              12 => "🕛"
            }
    Map.get(clocks, hour)
  end
end