# rootulp's solution

## to Clock in the Go Track

Published at Jun 10 2020 · 0 comments
Instructions
Test suite
Solution

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.

## Coding the solution

Look for a stub file having the name clock.go and place your solution code in that file.

## Running the tests

To run the tests run the command `go test` from within the exercise directory.

If the test suite contains benchmarks, you can run these with the `--bench` and `--benchmem` flags:

``````go test -v --bench . --benchmem
``````

Keep in mind that each reviewer will run benchmarks on a different machine, with different specs, so the results from these benchmark tests may vary.

## Further information

For more detailed information about the Go track, including how to get help if you're having trouble, please visit the exercism.io Go language page.

## 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.

### cases_test.go

``````package clock

// Source: exercism/problem-specifications
// Commit: b344762 clock: Add test case for exactly negative sixty minutes.
// Problem Specifications Version: 2.4.0

// Create a new clock with an initial time
var timeTests = []struct {
h, m int
want string
}{
{8, 0, "08:00"},        // on the hour
{11, 9, "11:09"},       // past the hour
{24, 0, "00:00"},       // midnight is zero hours
{25, 0, "01:00"},       // hour rolls over
{100, 0, "04:00"},      // hour rolls over continuously
{1, 60, "02:00"},       // sixty minutes is next hour
{0, 160, "02:40"},      // minutes roll over
{0, 1723, "04:43"},     // minutes roll over continuously
{25, 160, "03:40"},     // hour and minutes roll over
{201, 3001, "11:01"},   // hour and minutes roll over continuously
{72, 8640, "00:00"},    // hour and minutes roll over to exactly midnight
{-1, 15, "23:15"},      // negative hour
{-25, 0, "23:00"},      // negative hour rolls over
{-91, 0, "05:00"},      // negative hour rolls over continuously
{1, -40, "00:20"},      // negative minutes
{1, -160, "22:20"},     // negative minutes roll over
{1, -4820, "16:40"},    // negative minutes roll over continuously
{2, -60, "01:00"},      // negative sixty minutes is previous hour
{-25, -160, "20:20"},   // negative hour and minutes both roll over
{-121, -5810, "22:10"}, // negative hour and minutes both roll over continuously
}

h, m, a int
want    string
}{
{10, 0, 3, "10:03"},    // add minutes
{6, 41, 0, "06:41"},    // add no minutes
{0, 45, 40, "01:25"},   // add to next hour
{10, 0, 61, "11:01"},   // add more than one hour
{0, 45, 160, "03:25"},  // add more than two hours with carry
{23, 59, 2, "00:01"},   // add across midnight
{5, 32, 1500, "06:32"}, // add more than one day (1500 min = 25 hrs)
{1, 1, 3500, "11:21"},  // add more than two days
}

// Subtract minutes
var subtractTests = []struct {
h, m, a int
want    string
}{
{10, 3, 3, "10:00"},    // subtract minutes
{10, 3, 30, "09:33"},   // subtract to previous hour
{10, 3, 70, "08:53"},   // subtract more than an hour
{0, 3, 4, "23:59"},     // subtract across midnight
{0, 0, 160, "21:20"},   // subtract more than two hours
{6, 15, 160, "03:35"},  // subtract more than two hours with borrow
{5, 32, 1500, "04:32"}, // subtract more than one day (1500 min = 25 hrs)
{2, 20, 3000, "00:20"}, // subtract more than two days
}

// Compare two clocks for equality
type hm struct{ h, m int }

var eqTests = []struct {
c1, c2 hm
want   bool
}{
// clocks with same time
{
hm{15, 37},
hm{15, 37},
true,
},
// clocks a minute apart
{
hm{15, 36},
hm{15, 37},
false,
},
// clocks an hour apart
{
hm{14, 37},
hm{15, 37},
false,
},
// clocks with hour overflow
{
hm{10, 37},
hm{34, 37},
true,
},
// clocks with hour overflow by several days
{
hm{3, 11},
hm{99, 11},
true,
},
// clocks with negative hour
{
hm{22, 40},
hm{-2, 40},
true,
},
// clocks with negative hour that wraps
{
hm{17, 3},
hm{-31, 3},
true,
},
// clocks with negative hour that wraps multiple times
{
hm{13, 49},
hm{-83, 49},
true,
},
// clocks with minute overflow
{
hm{0, 1},
hm{0, 1441},
true,
},
// clocks with minute overflow by several days
{
hm{2, 2},
hm{2, 4322},
true,
},
// clocks with negative minute
{
hm{2, 40},
hm{3, -20},
true,
},
// clocks with negative minute that wraps
{
hm{4, 10},
hm{5, -1490},
true,
},
// clocks with negative minute that wraps multiple times
{
hm{6, 15},
hm{6, -4305},
true,
},
// clocks with negative hours and minutes
{
hm{7, 32},
hm{-12, -268},
true,
},
// clocks with negative hours and minutes that wrap
{
hm{18, 7},
hm{-54, -11513},
true,
},
// full clock and zeroed clock
{
hm{24, 0},
hm{0, 0},
true,
},
}``````

### clock_test.go

``````package clock

import (
"reflect"
"strconv"
"strings"
"testing"
)

// Clock API:
//
// type Clock                      // define the clock type
// New(hour, minute int) Clock     // a "constructor"
// (Clock) String() string         // a "stringer"
// (Clock) Subtract(minutes int) Clock
//
// To satisfy the README requirement about clocks being equal, values of
// your Clock type need to work with the == operator. This means that if your
// New function returns a pointer rather than a value, your clocks will
// probably not work with ==.
//
// While the time.Time type in the standard library (https://golang.org/pkg/time/#Time)
// doesn't necessarily need to be used as a basis for your Clock type, it might
// help to look at how constructors there (Date and Now) return values rather
// than pointers. Note also how most time.Time methods have value receivers
//
// For some useful guidelines on when to use a value receiver or a pointer

func TestCreateClock(t *testing.T) {
for _, n := range timeTests {
if got := New(n.h, n.m); got.String() != n.want {
t.Fatalf("New(%d, %d) = %q, want %q", n.h, n.m, got, n.want)
}
}
t.Log(len(timeTests), "test cases")
}

for _, a := range addTests {
if got := New(a.h, a.m).Add(a.a); got.String() != a.want {
t.Fatalf("New(%d, %d).Add(%d) = %q, want %q",
a.h, a.m, a.a, got, a.want)
}
}
}

func TestSubtractMinutes(t *testing.T) {
for _, a := range subtractTests {
if got := New(a.h, a.m).Subtract(a.a); got.String() != a.want {
t.Fatalf("New(%d, %d).Subtract(%d) = %q, want %q",
a.h, a.m, a.a, got, a.want)
}
}
t.Log(len(subtractTests), "test cases")
}

for _, a := range addTests {
var wantHour, wantMin int
split := strings.SplitN(a.want, ":", 2)
if len(split) > 0 {
wantHour, _ = strconv.Atoi(split[0])
}
if len(split) > 1 {
wantMin, _ = strconv.Atoi(split[1])
}
want := New(wantHour, wantMin)
if got := New(a.h, a.m).Add(a.a); !reflect.DeepEqual(got, want) {
t.Fatalf("New(%d, %d).Add(%d) = %v, want %v",
a.h, a.m, a.a, got, want)
}
}
}

func TestSubtractMinutesStringless(t *testing.T) {
for _, a := range subtractTests {
var wantHour, wantMin int
split := strings.SplitN(a.want, ":", 2)
if len(split) > 0 {
wantHour, _ = strconv.Atoi(split[0])
}
if len(split) > 1 {
wantMin, _ = strconv.Atoi(split[1])
}
want := New(wantHour, wantMin)
if got := New(a.h, a.m).Subtract(a.a); !reflect.DeepEqual(got, want) {
t.Fatalf("New(%d, %d).Subtract(%d) = %v, want %v",
a.h, a.m, a.a, got, want)
}
}
t.Log(len(subtractTests), "test cases")
}

func TestCompareClocks(t *testing.T) {
for _, e := range eqTests {
clock1 := New(e.c1.h, e.c1.m)
clock2 := New(e.c2.h, e.c2.m)
got := clock1 == clock2
if got != e.want {
t.Log("Clock1:", clock1)
t.Log("Clock2:", clock2)
t.Logf("Clock1 == Clock2 is %t, want %t", got, e.want)
if reflect.DeepEqual(clock1, clock2) {
}
t.FailNow()
}
}
t.Log(len(eqTests), "test cases")
}

clock2 := New(16, 1)
if !reflect.DeepEqual(clock1, clock2) {
}
}

func TestSubtractAndCompare(t *testing.T) {
clock1 := New(16, 1).Subtract(16)
clock2 := New(15, 45)
if !reflect.DeepEqual(clock1, clock2) {
t.Errorf("clock.New(16,1).Subtract(16) differs from clock.New(15,45)")
}
}

c := New(12, 0)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, a := range addTests {
}
}
}

func BenchmarkSubtractMinutes(b *testing.B) {
c := New(12, 0)
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, a := range subtractTests {
c.Subtract(a.a)
}
}
}

func BenchmarkCreateClocks(b *testing.B) {
for i := 0; i < b.N; i++ {
for _, n := range timeTests {
New(n.h, n.m)
}
}
}``````

### example_clock_test.go

``````package clock

import "fmt"

func ExampleClock_new() {
// a new clock
clock1 := New(10, 30)
fmt.Println(clock1.String())

// Output: 10:30
}

// create a clock
clock := New(10, 30)

// add 30 minutes to it
fmt.Println(clock.String())

// Output: 11:00
}

func ExampleClock_Subtract() {
// create a clock
clock := New(10, 30)

// subtract an hour and a half from it
clock = clock.Subtract(90)
fmt.Println(clock.String())

// Output: 09:00
}

func ExampleClock_compare() {
// a new clock
clock1 := New(10, 30)

// a second clock, same as the first
clock2 := New(10, 30)

// are the clocks equal?
fmt.Println(clock2 == clock1)

// change the second clock

// are the clocks equal now?
fmt.Println(clock2 == clock1)

// Output:
// true
// false
}``````
``````package clock

import (
"fmt"
"time"
)

// Clock defines a type for keeping time.
type Clock struct {
hour int
min  int
}

// New is a constructor to create instances of Clock.
func New(hour, min int) Clock {
t := time.Date(0, 0, 0, hour, min, 0, 0, time.UTC)
return Clock{hour: t.Hour(), min: t.Minute()}
}

// Add moves the clock forward by the provided number of minutes.
func (clock Clock) Add(minutes int) Clock {
return Clock{hour: t.Hour(), min: t.Minute()}
}

// Subtract moves the clock backward by the provided number of minutes.
func (clock Clock) Subtract(minutes int) Clock {
return Clock{hour: t.Hour(), min: t.Minute()}
}

func (clock Clock) String() string {
return clock.time().Format("15:04")
}

// time converts the clock to a time.Time instance.
func (clock Clock) time() time.Time {
return time.Date(0, 0, 0, clock.hour, clock.min, 0, 0, time.UTC)
}

func durationFromMinutes(minutes int) time.Duration {
d, _ := time.ParseDuration(fmt.Sprintf("%dm", minutes)) // WARNING: Ignoring errors
return d
}``````