Swift DateComponentsFormatter 删除前导零,但在分钟位置至少保留一位数字



我在 Swift 中使用以下 DateComponentsFormatter:

let formatter = DateComponentsFormatter()
formatter.unitsStyle   = .positional
formatter.allowedUnits = [.hour, .minute, .second]
formatter.zeroFormattingBehavior = [.default]

这将生成 2:41(2 分 41 秒(和 08(8 秒(等结果。但是,我想在分钟位置至少保留一位数字,返回 0:08 而不是 08。使用DateComponents格式化程序可以做到这一点吗?我更喜欢返回本地化结果的解决方案。

是的。这可以很容易地完成,在设置日期组件格式化程序允许的单位时,根据时间间隔添加三元条件:


Xcode 11 • Swift 5.1

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]
        let string = Formatter.positional.string(from: self)!
        return string.hasPrefix("0") && string.count > 4 ?
            .init(string.dropFirst()) : string
    }
}

用法

8.0.positionalTime     //    "0:08"
161.0.positionalTime   //    "2:41"
3600.0.positionalTime  // "1:00:00"

可以通过根据持续时间修改allowedUnitszeroFormattingBehavior来创建适当的位置时间字符串。请注意,当希望只有一个零时,这将在几分钟内填充两个前导零,以便以后以安全的方式处理这种情况。

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
}

以下是durationformattedDuration的列表:

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"

提出了这个解决方案,使用正则表达式,似乎涵盖了所有情况:

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

查看不同的 zeroFormattingBehavior 属性值。我用它来获得我认为你正在寻找的结果,但必要时进行实验......

    formatter.zeroFormattingBehavior = [.pad]

最新更新