🎉 Exercism Research is now launched. Help Exercism, help science and have some fun at research.exercism.io 🎉
Avatar of domnikl

domnikl's solution

to Clock in the Kotlin Track

Published at Nov 16 2019 · 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.

Setup

Go through the setup instructions for Kotlin to install the necessary dependencies:

https://exercism.io/tracks/kotlin/installation

Making the test suite pass

Execute the tests with:

$ gradlew test

Use gradlew.bat if you're on Windows

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

Once you get a test passing, you can enable the next one by removing the @Ignore annotation.

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.

ClockAddTest.kt

import org.junit.Ignore
import org.junit.Test
import kotlin.test.assertEquals

class ClockAddTest {

    @Ignore
    @Test
    fun addMinutes() {
        val clock = Clock(10, 0)
        clock.add(3)
        assertEquals("10:03", clock.toString())
    }

    @Ignore
    @Test
    fun addNoMinutes() {
        val clock = Clock(6, 41)
        clock.add(0)
        assertEquals("06:41", clock.toString())
    }

    @Ignore
    @Test
    fun addToNextHour() {
        val clock = Clock(0, 45)
        clock.add(40)
        assertEquals("01:25", clock.toString())
    }

    @Ignore
    @Test
    fun addMoreThanOneHour() {
        val clock = Clock(10, 0)
        clock.add(61)
        assertEquals("11:01", clock.toString())
    }

    @Ignore
    @Test
    fun addMoreThanTwoHoursWithCarry() {
        val clock = Clock(0, 45)
        clock.add(160)
        assertEquals("03:25", clock.toString())
    }

    @Ignore
    @Test
    fun addAcrossMidnight() {
        val clock = Clock(23, 59)
        clock.add(2)
        assertEquals("00:01", clock.toString())
    }

    @Ignore
    @Test
    fun addMoreThanOneDay() {
        val clock = Clock(5, 32)
        clock.add(1500)
        assertEquals("06:32", clock.toString())
    }

    @Ignore
    @Test
    fun addMoreThanTwoDays() {
        val clock = Clock(1, 1)
        clock.add(3500)
        assertEquals("11:21", clock.toString())
    }



}

ClockCreationTest.kt

import org.junit.Ignore
import org.junit.Test
import kotlin.test.assertEquals

class ClockCreationTest {

    @Test
    fun canPrintTimeOnTheHour() {
        assertEquals("08:00", Clock(8, 0).toString())
    }

    @Ignore
    @Test
    fun canPrintTimeWithMinutes() {
        assertEquals("11:09", Clock(11, 9).toString())
    }

    @Ignore
    @Test
    fun midnightPrintsAsZero() {
        assertEquals("00:00", Clock(24, 0).toString())
    }

    @Ignore
    @Test
    fun hourRollsOver() {
        assertEquals("01:00", Clock(25, 0).toString())
    }

    @Ignore
    @Test
    fun hourRollsOverContinuously() {
        assertEquals("04:00", Clock(100, 0).toString())
    }

    @Ignore
    @Test
    fun sixtyMinutesIsNextHour() {
        assertEquals("02:00", Clock(1, 60).toString())
    }

    @Ignore
    @Test
    fun minutesRollOver() {
        assertEquals("02:40", Clock(0, 160).toString())
    }

    @Ignore
    @Test
    fun minutesRollOverContinuously() {
        assertEquals("04:43", Clock(0, 1723).toString())
    }

    @Ignore
    @Test
    fun hourAndMinutesRollOver() {
        assertEquals("03:40", Clock(25, 160).toString())
    }

    @Ignore
    @Test
    fun hourAndMinutesRollOverContinuously() {
        assertEquals("11:01", Clock(201, 3001).toString())
    }

    @Ignore
    @Test
    fun hourAndMinutesRollOverToExactlyMidnight() {
        assertEquals("00:00", Clock(72, 8640).toString())
    }

    @Ignore
    @Test
    fun negativeHour() {
        assertEquals("23:15", Clock(-1, 15).toString())
    }

    @Ignore
    @Test
    fun negativeHourRollsOver() {
        assertEquals("23:00", Clock(-25, 0).toString())
    }

    @Ignore
    @Test
    fun negativeHourRollsOverContinuously() {
        assertEquals("05:00", Clock(-91, 0).toString())
    }

    @Ignore
    @Test
    fun negativeMinutes() {
        assertEquals("00:20", Clock(1, -40).toString())
    }

    @Ignore
    @Test
    fun negativeMinutesRollOver() {
        assertEquals("22:20", Clock(1, -160).toString())
    }

    @Ignore
    @Test
    fun negativeMinutesRollOverContinuously() {
        assertEquals("16:40", Clock(1, -4820).toString())
    }

    @Ignore
    @Test
    fun negativeHourAndMinutesBothRollOver() {
        assertEquals("20:20", Clock(-25, -160).toString())
    }

    @Ignore
    @Test
    fun negativeHourAndMinutesBothRollOverContinuously() {
        assertEquals("22:10", Clock(-121, -5810).toString())
    }

    @Ignore
    @Test
    fun fullClockAndZeroedClock(){
        val first = Clock(24,0)
        val second = Clock(0,0)
        assertEquals(first, second)
    }

}

ClockEqualTest.kt

import org.junit.Ignore
import org.junit.Test
import kotlin.test.assertNotEquals
import kotlin.test.assertEquals

class ClockEqualTest {

    @Ignore
    @Test
    fun clocksWithSameTimeAreEqual() {
        assertEquals(Clock(15, 37), Clock(15, 37))
    }

    @Ignore
    @Test
    fun clocksAMinuteApartAreNotEqual() {
        assertNotEquals(Clock(15, 36), Clock(15, 37))
    }

    @Ignore
    @Test
    fun clocksAnHourApartAreNotEqual() {
        assertNotEquals(Clock(14, 37), Clock(15, 37))
    }

    @Ignore
    @Test
    fun clocksWithHourOverflow() {
        assertEquals(Clock(10, 37), Clock(34, 37))
    }

    @Ignore
    @Test
    fun clocksWithHourOverflowBySeveralDays() {
        assertEquals(Clock(3, 11), Clock(99, 11))
    }

    @Ignore
    @Test
    fun clocksWithNegateHour() {
        assertEquals(Clock(22, 40), Clock(-2, 40))
    }

    @Ignore
    @Test
    fun clocksWithNegativeHourThatWraps() {
        assertEquals(Clock(17, 3), Clock(-31, 3))
    }

    @Ignore
    @Test
    fun clocksWithNegativeHourThatWrapsMultipleTimes() {
        assertEquals(Clock(13, 49), Clock(-83, 49))
    }

    @Ignore
    @Test
    fun clocksWithMinuteOverflow() {
        assertEquals(Clock(0, 1), Clock(0, 1441))
    }

    @Ignore
    @Test
    fun clocksWithMinuteOverflowBySeveralDays() {
        assertEquals(Clock(2, 2), Clock(2, 4322))
    }

    @Ignore
    @Test
    fun clocksWithNegativeMinutes() {
        assertEquals(Clock(2, 40), Clock(3, -20))
    }

    @Ignore
    @Test
    fun clocksWithNegativeMinutesThatWraps() {
        assertEquals(Clock(4, 10), Clock(5, -1490))
    }

    @Ignore
    @Test
    fun clocksWithNegativeMinutesThatWrapsMultipleTimes() {
        assertEquals(Clock(6, 15), Clock(6, -4305))
    }

    @Ignore
    @Test
    fun clocksWithNegativeHoursAndMinutes() {
        assertEquals(Clock(7, 32), Clock(-12, -268))
    }

    @Ignore
    @Test
    fun clocksWithNegativeHoursAndMinutesThatWrap() {
        assertEquals(Clock(18, 7), Clock(-54, -11513))
    }

}

ClockSubtractTest.kt

import org.junit.Ignore
import org.junit.Test
import kotlin.test.assertEquals

class ClockSubtractTest{

    @Ignore
    @Test
    fun subtractMinutes() {
        val clock = Clock(10, 3)
        clock.subtract(3)
        assertEquals("10:00", clock.toString())
    }

    @Ignore
    @Test
    fun subtractToPreviousHour() {
        val clock = Clock(10, 3)
        clock.subtract(30)
        assertEquals("09:33", clock.toString())
    }

    @Ignore
    @Test
    fun subtractAcrossMidnight() {
        val clock = Clock(0, 3)
        clock.subtract(4)
        assertEquals("23:59", clock.toString())
    }

    @Ignore
    @Test
    fun subtractMoreThanTwoHours() {
        val clock = Clock(0, 0)
        clock.subtract(160)
        assertEquals("21:20", clock.toString())
    }

    @Ignore
    @Test
    fun subtractMoreThanTwoHoursWithBorrow() {
        val clock = Clock(6, 15)
        clock.subtract(160)
        assertEquals("03:35", clock.toString())
    }

    @Ignore
    @Test
    fun subtractMoreThanOneDay() {
        val clock = Clock(5, 32)
        clock.subtract(1500)
        assertEquals("04:32", clock.toString())
    }

    @Ignore
    @Test
    fun subtractMoreThanTwoDays() {
        val clock = Clock(2, 20)
        clock.subtract(3000)
        assertEquals("00:20", clock.toString())
    }
}
import kotlin.math.abs

class Clock(private var hours: Int, private var minutes: Int) {
    init {
        normalize()
    }

    override fun equals(other: Any?) = when(other) {
        is Clock -> hours == other.hours && minutes == other.minutes
        else -> false
    }

    override fun toString(): String {
        return "%02d:%02d".format(hours, minutes)
    }

    fun subtract(minutes: Int) {
        this.minutes -= minutes
        normalize()
    }

    fun add(minutes: Int) {
        this.minutes += minutes
        normalize()
    }

    private fun normalize() {
        if (abs(minutes) >= 60) {
            hours += minutes / 60
            minutes %= 60
        }

        if (minutes < 0) {
            hours -= 1
            minutes += 60
        }

        if (abs(hours) >= 24) {
            hours %= 24
        }

        if (hours < 0) {
            hours += 24
        }
    }
}

Community comments

Find this solution interesting? Ask the author a question to learn more.

What can you learn from this solution?

A huge amount can be learned from reading other people’s code. This is why we wanted to give exercism users the option of making their solutions public.

Here are some questions to help you reflect on this solution and learn the most from it.

  • What compromises have been made?
  • Are there new concepts here that you could read more about to improve your understanding?