1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
|
package at.hannibal2.skyhanni.utils
import at.hannibal2.skyhanni.utils.NumberUtil.addSeparators
import at.hannibal2.skyhanni.utils.StringUtils.matchMatcher
import kotlin.time.Duration
object TimeUtils {
private val pattern =
"(?:(?<y>\\d+) ?y(?:\\w* ?)?)?(?:(?<d>\\d+) ?d(?:\\w* ?)?)?(?:(?<h>\\d+) ?h(?:\\w* ?)?)?(?:(?<m>\\d+) ?m(?:\\w* ?)?)?(?:(?<s>\\d+) ?s(?:\\w* ?)?)?".toPattern()
fun formatDuration(
duration: Duration,
biggestUnit: TimeUnit = TimeUnit.YEAR,
showMilliSeconds: Boolean = false,
longName: Boolean = false,
maxUnits: Int = -1
): String = formatDuration(
duration.inWholeMilliseconds - 999, biggestUnit, showMilliSeconds, longName, maxUnits
)
fun formatDuration(
millis: Long,
biggestUnit: TimeUnit = TimeUnit.YEAR,
showMilliSeconds: Boolean = false,
longName: Boolean = false,
maxUnits: Int = -1
): String {
// TODO: if this weird offset gets removed, also remove that subtraction from formatDuration(kotlin.time.Duration)
var milliseconds = millis + 999
val map = mutableMapOf<TimeUnit, Int>()
for (unit in TimeUnit.values()) {
if (unit.ordinal >= biggestUnit.ordinal) {
val factor = unit.factor
map[unit] = (milliseconds / factor).toInt()
milliseconds %= factor
}
}
val builder = StringBuilder()
var count = 0
for ((unit, value) in map.entries) {
if (value > 0 || builder.isNotEmpty() || unit == TimeUnit.SECOND) {
builder.append(value.addSeparators())
val name = if (longName) {
" " + unit.longName + if (value > 1) "s" else ""
} else {
unit.shortName
}
if (unit == TimeUnit.SECOND) {
if (showMilliSeconds) {
val formatMillis = milliseconds / 100
builder.append(".")
builder.append(formatMillis)
}
builder.append(name)
} else {
builder.append("$name ")
}
count++
if (maxUnits != -1) {
if (count == maxUnits) break
}
}
}
return builder.toString().trim()
}
// TODO: use kotlin Duration
fun getMillis(string: String) = pattern.matchMatcher(string.lowercase().trim()) {
val years = group("y")?.toLong() ?: 0L
val days = group("d")?.toLong() ?: 0L
val hours = group("h")?.toLong() ?: 0L
val minutes = group("m")?.toLong() ?: 0L
val seconds = group("s")?.toLong() ?: 0L
var millis = 0L
millis += seconds * 1000
millis += minutes * 60 * 1000
millis += hours * 60 * 60 * 1000
millis += days * 24 * 60 * 60 * 1000
millis += (years * 365.25 * 24 * 60 * 60 * 1000).toLong()
millis
} ?: tryAlternativeFormat(string)
private fun tryAlternativeFormat(string: String): Long {
val split = string.split(":")
return when (split.size) {
3 -> {
val hours = split[0].toInt() * 1000 * 60 * 60
val minutes = split[1].toInt() * 1000 * 60
val seconds = split[2].toInt() * 1000
seconds + minutes + hours
}
2 -> {
val minutes = split[0].toInt() * 1000 * 60
val seconds = split[1].toInt() * 1000
seconds + minutes
}
1 -> {
split[0].toInt() * 1000
}
else -> {
throw RuntimeException("Invalid format: '$string'")
}
}.toLong()
}
}
private const val FACTOR_SECONDS = 1000L
private const val FACTOR_MINUTES = FACTOR_SECONDS * 60
private const val FACTOR_HOURS = FACTOR_MINUTES * 60
private const val FACTOR_DAYS = FACTOR_HOURS * 24
private const val FACTOR_YEARS = (FACTOR_DAYS * 365.25).toLong()
enum class TimeUnit(val factor: Long, val shortName: String, val longName: String) {
YEAR(FACTOR_YEARS, "y", "Year"),
DAY(FACTOR_DAYS, "d", "Day"),
HOUR(FACTOR_HOURS, "h", "Hour"),
MINUTE(FACTOR_MINUTES, "m", "Minute"),
SECOND(FACTOR_SECONDS, "s", "Second"),
;
}
|