It is possible to create an appropriate positional time string by modifying the allowedUnits
and zeroFormattingBehavior
based on the duration. Note that this will pad two leading zeros for minutes when it's desirable to only have one, so that case is handled afterwards in a safe manner.
let formatter = DateComponentsFormatter()
if duration < 60 {
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .pad //pad seconds and minutes ie 00:05
} else if duration >= 60*60 {
formatter.allowedUnits = [.hour, .minute, .second]
}
var formattedDuration = formatter.string(from: duration) ?? "0"
if formattedDuration.hasPrefix("00") {
formattedDuration = String(formattedDuration.dropFirst()) //remove leading 0 ie 0:05
}
Here's a list of duration
s and formattedDuration
s:
0 ▶️ 0:00
5 ▶️ 0:05
30 ▶️ 0:30
60 ▶️ 1:00
65 ▶️ 1:05
3600 ▶️ 1:00:00
3665 ▶️ 1:00:05
3660 ▶️ 1:01:00
Alternate Solution
You can achieve the same result without checking for two leading zeros by utilizing the .dropTrailing
zeroFormattingBehavior
, interestingly enough. I will caution I don't understand exactly why this works and do not know what impact it may have on localization. Note that if duration is 0
, the result is "0"
, so we can use .pad
in this case to get a result of "00:00"
instead. With that you end up in the same situation noted above but maybe you will feel that is an edge case that may not need handling.
let formatter = DateComponentsFormatter()
if duration >= 60 {
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = .default
} else if duration == 0 {
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .pad
//is 00:00 but should be 0:00
} else {
formatter.allowedUnits = [.minute, .second]
formatter.zeroFormattingBehavior = .dropTrailing //magic here
}
let formattedDuration = formatter.string(from: duration) ?? "0"