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