Swift DateComponentsFormatter отбрасывает начальные нули, но сохраняет хотя бы одну цифру в минутах
Я использую следующий DateComponentsFormatter в Swift:
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = [.default]
Это приводит к результатам как 2:41 (в течение 2 минут и 41 секунды) и 08 (в течение 8 секунд). Однако я хотел бы сохранить хотя бы одну цифру в месте "Минуты", возвращая 0:08 вместо 08. Возможно ли это с DateComponentsFormatter? Я бы предпочел решение, которое возвращает локализованный результат.
4 ответа
Да. Это может быть легко достигнуто добавлением троичного условного выражения на основе временного интервала при установке допустимых единиц форматирования компонентов даты:
extension Formatter {
static let positional: DateComponentsFormatter = {
let positional = DateComponentsFormatter()
positional.unitsStyle = .positional
positional.zeroFormattingBehavior = .pad
return positional
}()
}
extension TimeInterval {
var positionalTime: String {
Formatter.positional.allowedUnits = self >= 3600 ?
[.hour, .minute, .second] :
[.minute, .second]
return Formatter.positional.string(from: self)!
}
}
использование
8.0.positionalTime // "0:08"
161.0.positionalTime // "2:41"
3600.0.positionalTime // "1:00:00"
Придумал это решение с использованием регулярного выражения, которое, по-видимому, охватывает все случаи:
let formatter = DateComponentsFormatter()
formatter.allowedUnits = [.hour, .minute, .second]
formatter.unitsStyle = .positional
formatter.zeroFormattingBehavior = .pad
func durationString(for duration: TimeInterval) -> String {
formatter.string(from: duration)!
.replacingOccurrences(of: #"^00:0?|^0"#, with: "", options: .regularExpression)
}
durationString(for: 0) // 0:00
durationString(for: 1) // 0:01
durationString(for: 10) // 0:10
durationString(for: 60) // 1:00
durationString(for: 600) // 10:00
durationString(for: 3600) // 1:00:00
durationString(for: 36000) // 10:00:00
Можно создать соответствующую строку позиционного времени, изменив allowedUnits
а также zeroFormattingBehavior
в зависимости от продолжительности. Обратите внимание, что это приведет к добавлению двух ведущих нулей в течение нескольких минут, когда желательно иметь только один, так что впоследствии этот случай обрабатывается безопасным образом.
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
}
Вот список duration
s и 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
Альтернативное решение
Вы можете достичь того же результата без проверки двух ведущих нулей, используя .dropTrailing
zeroFormattingBehavior
, как ни странно. Я предупреждаю, что не понимаю, почему это работает, и не знаю, какое влияние это может оказать на локализацию. Обратите внимание, что если продолжительность0
, результат "0"
, поэтому мы можем использовать .pad
в этом случае получить результат "00:00"
вместо. При этом вы окажетесь в той же ситуации, о которой говорилось выше, но, возможно, вы почувствуете, что это крайний случай, который может не нуждаться в обработке.
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"
Проверьте другое значение свойства zeroFormattingBehavior. Я использую это, чтобы получить результат, который, я думаю, вы ищете, но экспериментируйте по мере необходимости...
formatter.zeroFormattingBehavior = [.pad]