🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉 # angelikatyborska's solution

## to Clock in the Elixir Track

Published at May 01 2021 · 0 comments
Instructions
Test suite
Solution

#### Note:

This exercise has changed since this solution was written.

Implement a clock that handles times without dates.

You should be able to add and subtract minutes to it.

Two clocks that represent the same time should be equal to each other.

## Running tests

Execute the tests with:

``````\$ elixir clock_test.exs
``````

### Pending tests

In the test suites, all but the first test have been skipped.

Once you get a test passing, you can unskip the next one by commenting out the relevant `@tag :pending` with a `#` symbol.

For example:

``````# @tag :pending
test "shouting" do
assert Bob.hey("WATCH OUT!") == "Whoa, chill out!"
end
``````

Or, you can enable all the tests by commenting out the `ExUnit.configure` line in the test suite.

``````# ExUnit.configure exclude: :pending, trace: true
``````

If you're stuck on something, it may help to look at some of the available resources out there where answers might be found.

## Source

Pairing session with Erin Drummond https://twitter.com/ebdrummond

## Submitting Incomplete Solutions

It's possible to submit an incomplete solution so you can see how others have completed the exercise.

### clock_test.exs

``````if !System.get_env("EXERCISM_TEST_EXAMPLES") do
end

ExUnit.start()
ExUnit.configure(exclude: :pending, trace: true)

defmodule ClockTest do
use ExUnit.Case

# @tag :pending
test "to_string" do
try do
to_string(%Clock{})
rescue
Protocol.UndefinedError ->
refute(true, """
Can't convert Clock to string.
Hint: implement the String.Chars protocol for Clock.
http://elixir-lang.org/getting-started/protocols.html
http://elixir-lang.org/docs/stable/elixir/String.Chars.html
""")
end
end

describe "create" do
@tag :pending
test "on the hour" do
assert Clock.new(8, 0) |> to_string == "08:00"
end

@tag :pending
test "past the hour" do
assert Clock.new(11, 9) |> to_string == "11:09"
end

@tag :pending
test "midnight is zero hours" do
assert Clock.new(24, 0) |> to_string == "00:00"
end

@tag :pending
test "hour rolls over" do
assert Clock.new(25, 0) |> to_string == "01:00"
end

@tag :pending
test "hour rolls over continuously" do
assert Clock.new(100, 0) |> to_string == "04:00"
end

@tag :pending
test "sixty minutes is next hour" do
assert Clock.new(1, 60) |> to_string == "02:00"
end

@tag :pending
test "minutes roll over" do
assert Clock.new(0, 160) |> to_string == "02:40"
end

@tag :pending
test "minutes roll over continuously" do
assert Clock.new(0, 1723) |> to_string == "04:43"
end

@tag :pending
test "hour and minutes roll over" do
assert Clock.new(25, 160) |> to_string == "03:40"
end

@tag :pending
test "hour and minutes roll over continuously" do
assert Clock.new(201, 3001) |> to_string == "11:01"
end

@tag :pending
test "hour and minutes roll over to exactly midnight" do
assert Clock.new(72, 8640) |> to_string == "00:00"
end

@tag :pending
test "negative hour" do
assert Clock.new(-1, 15) |> to_string == "23:15"
end

@tag :pending
test "negative hour rolls over" do
assert Clock.new(-25, 0) |> to_string == "23:00"
end

@tag :pending
test "negative hour rolls over continuously" do
assert Clock.new(-91, 0) |> to_string == "05:00"
end

@tag :pending
test "negative minutes" do
assert Clock.new(1, -40) |> to_string == "00:20"
end

@tag :pending
test "negative minutes roll over" do
assert Clock.new(1, -160) |> to_string == "22:20"
end

@tag :pending
test "negative minutes roll over continuously" do
assert Clock.new(1, -4820) |> to_string == "16:40"
end

@tag :pending
test "negative hour and minutes roll over" do
assert Clock.new(-25, -160) |> to_string == "20:20"
end

@tag :pending
test "negative hour and minutes roll over continuously" do
assert Clock.new(-121, -5810) |> to_string == "22:10"
end
end

@tag :pending
assert Clock.new(10, 0) |> Clock.add(3) |> to_string == "10:03"
end

@tag :pending
assert Clock.new(6, 41) |> Clock.add(0) |> to_string == "06:41"
end

@tag :pending
test "add to next hour" do
assert Clock.new(0, 45) |> Clock.add(40) |> to_string == "01:25"
end

@tag :pending
test "add more than one hour" do
assert Clock.new(10, 0) |> Clock.add(61) |> to_string == "11:01"
end

@tag :pending
test "add more than two hours with carry" do
assert Clock.new(0, 45) |> Clock.add(160) |> to_string == "03:25"
end

@tag :pending
assert Clock.new(23, 59) |> Clock.add(2) |> to_string == "00:01"
end

@tag :pending
test "add more than one day (1500 min = 25 hrs)" do
assert Clock.new(5, 32) |> Clock.add(1500) |> to_string == "06:32"
end

@tag :pending
test "add more than two days" do
assert Clock.new(1, 1) |> Clock.add(3500) |> to_string == "11:21"
end

@tag :pending
test "subtract minutes" do
assert Clock.new(10, 3) |> Clock.add(-3) |> to_string == "10:00"
end

@tag :pending
test "subtract to previous hour" do
assert Clock.new(10, 3) |> Clock.add(-30) |> to_string == "09:33"
end

@tag :pending
test "subtract more than an hour" do
assert Clock.new(10, 3) |> Clock.add(-70) |> to_string == "08:53"
end

@tag :pending
test "subtract across midnight" do
assert Clock.new(0, 3) |> Clock.add(-4) |> to_string == "23:59"
end

@tag :pending
test "subtract more than two hours" do
assert Clock.new(0, 0) |> Clock.add(-160) |> to_string == "21:20"
end

@tag :pending
test "subtract more than two hours with borrow" do
assert Clock.new(6, 15) |> Clock.add(-160) |> to_string == "03:35"
end

@tag :pending
test "subtract more than one day (1500 min = 25 hrs)" do
assert Clock.new(5, 32) |> Clock.add(-1500) |> to_string == "04:32"
end

@tag :pending
test "subtract more than two days" do
assert Clock.new(2, 20) |> Clock.add(-3000) |> to_string == "00:20"
end
end

describe "==" do
@tag :pending
test "clocks with same time" do
assert Clock.new(15, 37) == Clock.new(15, 37)
end

@tag :pending
test "clocks a minute apart" do
refute Clock.new(15, 36) == Clock.new(15, 37)
end

@tag :pending
test "clocks an hour apart" do
refute Clock.new(14, 37) == Clock.new(15, 37)
end

@tag :pending
test "clocks with hour overflow" do
assert Clock.new(10, 37) == Clock.new(34, 37)
end

@tag :pending
test "clocks with hour overflow by several days" do
assert Clock.new(3, 11) == Clock.new(99, 11)
end

@tag :pending
test "clocks with negative hour" do
assert Clock.new(22, 40) == Clock.new(-2, 40)
end

@tag :pending
test "clocks with negative hour that wraps" do
assert Clock.new(17, 3) == Clock.new(-31, 3)
end

@tag :pending
test "clocks with negative hour that wraps multiple times" do
assert Clock.new(13, 49) == Clock.new(-83, 49)
end

@tag :pending
test "clocks with minute overflow" do
assert Clock.new(0, 1) == Clock.new(0, 1441)
end

@tag :pending
test "clocks with minute overflow by several days" do
assert Clock.new(2, 2) == Clock.new(2, 4322)
end

@tag :pending
test "clocks with negative minute" do
assert Clock.new(2, 40) == Clock.new(3, -20)
end

@tag :pending
test "clocks with negative minute that wraps" do
assert Clock.new(4, 10) == Clock.new(5, -1490)
end

@tag :pending
test "clocks with negative minute that wraps multiple times" do
assert Clock.new(6, 15) == Clock.new(6, -4305)
end

@tag :pending
test "clocks with negative hours and minutes" do
assert Clock.new(7, 32) == Clock.new(-12, -268)
end

@tag :pending
test "clocks with negative hours and minutes that wrap" do
assert Clock.new(18, 7) == Clock.new(-54, -11513)
end
end
end``````
``````defmodule Clock do
@hours 24
@minutes 60

defstruct hour: 0, minute: 0

@doc """
Returns a string representation of a clock:

iex> Clock.new(8, 9) |> to_string
"08:09"
"""
@spec new(integer, integer) :: Clock
def new(hour, minute) do
%Clock{hour: hour, minute: minute}
|> rollover()
end

@doc """

"10:03"
"""
%Clock{hour: hour, minute: minute + add_minute}
|> rollover()
end

defp rollover(%Clock{hour: hour, minute: minute} = clock) do
case {hour, minute} do
{hour, minute} when minute >= @minutes ->
%Clock{hour: hour + 1, minute: minute - @minutes}
|> rollover()

{hour, minute} when minute < 0 ->
%Clock{hour: hour - 1, minute: minute + @minutes}
|> rollover()

{hour, minute} when hour >= @hours ->
%Clock{hour: hour - @hours, minute: minute}
|> rollover()

{hour, minute} when hour < 0 ->
%Clock{hour: hour + @hours, minute: minute}
|> rollover()

_ ->
clock
end
end
end

defimpl String.Chars, for: Clock do
def to_string(%Clock{hour: hour, minute: minute}) do
[hour, minute]
|> Enum.join(":")
end
end``````