Solution: Pretty time display
fun secondsToPrettyTime(seconds: Int): String {
if (seconds < 0) return "Invalid input"
if (seconds == 0) return "Now"
// or
// when {
// seconds < 0 -> return "Invalid input"
// seconds == 0 -> return "Now"
// }
val hours = seconds / SECONDS_IN_HOUR
val minutes = (seconds % SECONDS_IN_HOUR)/MINUTES_IN_HOUR
val secondsLeft = seconds % SECONDS_IN_MINUTE
var result = ""
if (hours > 0) {
result += "$hours h"
}
if (minutes > 0) {
result += " $minutes min"
}
if (secondsLeft > 0) {
result += " $secondsLeft sec"
}
return result.trim()
}
// Constants should be defined outside of the function
// we often name them using UPPER_SNAKE_CASE convention
// const modifier is a low-level optimization
private const val SECONDS_IN_MINUTE = 60
private const val MINUTES_IN_HOUR = 60
private const val SECONDS_IN_HOUR = 3600
This is not the only possible solution to this exercise. Especially transforming hours
, minutes
and secondsLeft
into a string can be done in many different ways. I will show you a few popular solutions to this problem. Some of them use functions like listOfNotNull
, joinToString
, takeIf
or buildString
that are presented in the Functional Kotlin book (the next book I recommend you reading after Kotlin Essentials). Have patience, you will learn about them later, for now you can just treat them as curious examples of Kotlin code.
// Option 1 - This option is similar to the original,
// but uses if as an expression, and adds strings with +
val hoursString = if (hours > 0) "$hours h " else ""
val minutesString = if (minutes > 0) "$minutes min " else ""
val secondsString =
if (secondsLeft > 0) "$secondsLeft sec " else ""
return (hoursString + minutesString + secondsString).trim()
// Option 2 - This option uses listOfNotNull to construct
// a list of non-null elements, and then transforms those
// elements into a single string using joinToString
return listOfNotNull(
if (hours > 0) "$hours h" else null,
if (minutes > 0) "$minutes min" else null,
if (secondsLeft > 0) "$secondsLeft sec" else null
).joinToString(separator = " ")
// Option 3 - This option is similar to the previous one,
// but uses takeIf to filter out null elements
return listOfNotNull(
"$hours h".takeIf { hours > 0 },
"$minutes min".takeIf { minutes > 0 },
"$secondsLeft sec".takeIf { secondsLeft > 0 },
).joinToString(separator = " ")
// Option 4 - This option uses buildString function
// to construct a string by appending it with next parts
// inside lambda expression block
return buildString {
if (hours > 0) append("$hours h ")
if (minutes > 0) append("$minutes min ")
if (secondsLeft > 0) append("$secondsLeft sec ")
}.trim()
// Option 5 - This is the most complicated option,
// because it covers all possible cases in a when-expression.
// I do not recommend using this solution, but I wanted to
// show it as an example of how when-expression can be used.
return when {
hours == 0 && minutes == 0 && secondsLeft == 0 -> ""
hours == 0 && minutes == 0 -> "$secondsLeft sec"
hours == 0 && secondsLeft == 0 -> "$minutes min"
minutes == 0 && secondsLeft == 0 -> "$hours h"
minutes == 0 -> "$hours h $secondsLeft sec"
secondsLeft == 0 -> "$hours h $minutes min"
hours == 0 -> "$minutes min $secondsLeft sec"
else -> "$hours h $minutes min $secondsLeft sec"
}
import org.junit.Test
import kotlin.test.assertEquals
fun secondsToPrettyTime(seconds: Int): String {
if (seconds < 0) return "Invalid input"
if (seconds == 0) return "Now"
// or
// when {
// seconds < 0 -> return "Invalid input"
// seconds == 0 -> return "Now"
// }
val hours = seconds / SECONDS_IN_HOUR
val minutes = (seconds % SECONDS_IN_HOUR)/MINUTES_IN_HOUR
val secondsLeft = seconds % SECONDS_IN_MINUTE
var result = ""
if (hours > 0) {
result += "$hours h"
}
if (minutes > 0) {
result += " $minutes min"
}
if (secondsLeft > 0) {
result += " $secondsLeft sec"
}
return result.trim()
}
// Constants should be defined outside of the function
// we often name them using UPPER_SNAKE_CASE convention
// const modifier is a low-level optimization
private const val SECONDS_IN_MINUTE = 60
private const val MINUTES_IN_HOUR = 60
private const val SECONDS_IN_HOUR = 3600
fun main() {
println(secondsToPrettyTime(-1)) // Invalid input
println(secondsToPrettyTime(0)) // Now
println(secondsToPrettyTime(45)) // 45 sec
println(secondsToPrettyTime(60)) // 1 min
println(secondsToPrettyTime(150)) // 2 min 30 sec
println(secondsToPrettyTime(1410)) // 23 min 30 sec
println(secondsToPrettyTime(60 * 60)) // 1 h
println(secondsToPrettyTime(3665)) // 1 h 1 min 5 sec
}
class PrettyTimeTest {
@Test
fun testNegativeSeconds() {
val seconds = -1
val expected = "Invalid input"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testOnlySeconds() {
val seconds = 45
val expected = "45 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testOnlyMinutes() {
val seconds = 60
val expected = "1 min"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testMinutesAndSeconds() {
val seconds = 150
val expected = "2 min 30 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testMinutesAndSecondsWithRemainder() {
val seconds = 1410
val expected = "23 min 30 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testOnlyHours() {
val seconds = 3600
val expected = "1 h"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testHoursMinutesAndSeconds() {
val seconds = 3665
val expected = "1 h 1 min 5 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testZeroSeconds() {
val seconds = 0
val expected = "Now"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testHoursMinutesSecondsWithZeroMinutes() {
val seconds = 3605
val expected = "1 h 5 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testHoursMinutesWithZeroSeconds() {
val seconds = 7200
val expected = "2 h"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testMinutesSecondsWithZeroHours() {
val seconds = 150
val expected = "2 min 30 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
@Test
fun testLargeValue() {
val seconds = 123456789
val expected = "34293 h 33 min 9 sec"
assertEquals(expected, secondsToPrettyTime(seconds))
}
}
Marcin Moskala is a highly experienced developer and Kotlin instructor as the founder of Kt. Academy, an official JetBrains partner specializing in Kotlin training, Google Developers Expert, known for his significant contributions to the Kotlin community. Moskala is the author of several widely recognized books, including "Effective Kotlin," "Kotlin Coroutines," "Functional Kotlin," "Advanced Kotlin," "Kotlin Essentials," and "Android Development with Kotlin."
Beyond his literary achievements, Moskala is the author of the largest Medium publication dedicated to Kotlin. As a respected speaker, he has been invited to share his insights at numerous programming conferences, including events such as Droidcon and the prestigious Kotlin Conf, the premier conference dedicated to the Kotlin programming language.