Skip to content

Instantly share code, notes, and snippets.

@maulikhirani
Last active February 3, 2021 06:43
Show Gist options
  • Save maulikhirani/01e1ae1c023ca7f6e3bdf6ba392cf400 to your computer and use it in GitHub Desktop.
Save maulikhirani/01e1ae1c023ca7f6e3bdf6ba392cf400 to your computer and use it in GitHub Desktop.
Format Date-Time Duration in words with Kotlin
/**
* Number of milliseconds in a standard second.
*/
const val MILLIS_PER_SECOND: Long = 1000
/**
* Number of milliseconds in a standard minute.
*/
const val MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND
/**
* Number of milliseconds in a standard hour.
*/
const val MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE
/**
* Number of milliseconds in a standard day.
*/
const val MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR
/**
* Helper class for Duration formatting in words
*
* @property durationInMills difference between two times in milliseconds
*/
class DateTimeDurationHelper(private var durationInMills: Long) {
constructor(startMills: Long, endMills: Long) : this(
if (startMills > endMills) {
startMills - endMills
} else {
endMills - startMills
}
)
init {
if (durationInMills < 0) {
durationInMills = -durationInMills
}
}
val days: Long = durationInMills / MILLIS_PER_DAY
val hours: Long = (durationInMills - (days * MILLIS_PER_DAY)) / MILLIS_PER_HOUR
val minutes: Long =
(durationInMills - (days * MILLIS_PER_DAY) - (hours * MILLIS_PER_HOUR)) / MILLIS_PER_MINUTE
val seconds: Long =
(durationInMills - (days * MILLIS_PER_DAY) - (hours * MILLIS_PER_HOUR) -
(minutes * MILLIS_PER_MINUTE)) / MILLIS_PER_SECOND
/**
* Formats the duration as string (i.e 1 day, 2 hours, 3 minutes, 20 seconds)
*
* Note: this function doesn't support units like week, month, or year. The maximum unit is 'day'
*
* @param separator between different duration units (default is comma ", ")
* @param lowestDurationUnit decides what should be the lowest unit in the duration,
* For example, if you set this value as MINUTE, the formatted duration won't include seconds
* @param durationLength decides maximum number of non-zero units to be returned. For example,
* if you set this as FIRST_TWO, and your actual duration is "0 days, 1 hour, 2 minutes, 3 seconds",
* it will return "1 hour, 2 minutes" as result
* @param durationFormatter decides different ways to describe the duration units,
* If set as FULL, the units will be written as "day(s), hour(s), minute(s), second(s)"
* If set as SHORT, the units will be written as "day(s), hr(s), min(s), sec(s)"
* @return
*/
fun durationInWords(
separator: String = ", ",
lowestDurationUnit: DurationUnit = DurationUnit.MINUTE,
durationLength: DurationLength = DurationLength.FULL,
durationFormatter: DurationFormatter = DurationFormatter.FULL
): String {
val daysPart = days.duration(DurationUnit.DAY, durationFormatter)
val hoursPart = hours.duration(DurationUnit.HOUR, durationFormatter)
val minutesPart = minutes.duration(DurationUnit.MINUTE, durationFormatter)
val secondsPart = seconds.duration(DurationUnit.SECOND, durationFormatter)
var list = listOf(daysPart, hoursPart, minutesPart, secondsPart)
list = when (lowestDurationUnit) {
DurationUnit.DAY -> list.dropLast(3)
DurationUnit.HOUR -> list.dropLast(2)
DurationUnit.MINUTE -> list.dropLast(1)
else -> list
}
list = list.dropWhile { it.startsWith("0") }.dropLastWhile { it.startsWith("0") }
if (durationLength == DurationLength.FIRST_TWO) {
list = list.take(2).dropLastWhile { it.startsWith("0") }
} else if (durationLength == DurationLength.FIRST_THREE) {
list = list.take(3).dropLastWhile { it.startsWith("0") }
}
var durationString = list.joinToString(separator)
if (list.isEmpty()) {
durationString = 0L.duration(lowestDurationUnit, durationFormatter)
}
return durationString
}
private fun Long.duration(
durationUnit: DurationUnit,
durationFormatter: DurationFormatter
): String {
var duration = "$this ${durationUnit.getFormattedDurationUnit(durationFormatter)}"
if (this != 1L) {
duration = duration.plus("s")
}
return duration
}
private fun DurationUnit.getFormattedDurationUnit(durationFormatter: DurationFormatter): String {
return when (durationFormatter) {
DurationFormatter.FULL -> {
when (this) {
DurationUnit.DAY -> "day"
DurationUnit.HOUR -> "hour"
DurationUnit.MINUTE -> "minute"
DurationUnit.SECOND -> "second"
}
}
DurationFormatter.SHORT -> {
when (this) {
DurationUnit.DAY -> "day"
DurationUnit.HOUR -> "hr"
DurationUnit.MINUTE -> "min"
DurationUnit.SECOND -> "sec"
}
}
}
}
enum class DurationLength {
FULL,
FIRST_TWO,
FIRST_THREE
}
enum class DurationFormatter {
FULL,
SHORT
}
enum class DurationUnit {
DAY,
HOUR,
MINUTE,
SECOND,
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment