【问题标题】:Swift DateComponentsFormatter drop leading zeroes but keep at least one digit in Minutes placeSwift DateComponentsFormatter 删除前导零,但在 Minutes 位置至少保留一位数字
【发布时间】:2019-07-05 14:07:50
【问题描述】:

我在 Swift 中使用以下 DateComponentsFormatter:

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

这会产生像 2:41(2 分 41 秒)和 08(8 秒)这样的结果。但是,我希望在 Minutes 位置至少保留一位数字,返回 0:08 而不是 08。这可以使用 DateComponentsFormatter 吗?我更喜欢返回本地化结果的解决方案。

【问题讨论】:

    标签: ios swift date nsdatecomponents nsdatecomponentsformatter


    【解决方案1】:

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


    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"
    

    【讨论】:

    • 感谢 Leo,正是我想要的!
    • @jhk 我去查一下
    • 奇怪的是它返回“01:00:00”而不是“1:00:00”
    • 是的 - 在我查看应用程序的旧屏幕截图之前,我快疯了!
    • 如果你不想要那个前导零,我认为你将不得不手动处理它
    【解决方案2】:

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

    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
    

    【讨论】:

      【解决方案3】:

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

          formatter.zeroFormattingBehavior = [.pad]
      

      【讨论】:

      • 这将同时显示小时数 "0:00:08" 而不是 "0:08"。即使您将 DateComponentsFormatter maximumUnitCount 属性设置为 2 也无法解决使用位置单位样式时的问题。
      【解决方案4】:

      可以通过根据持续时间修改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
      }
      

      这是durations 和formattedDurations 的列表:

      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"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-16
        • 1970-01-01
        • 2011-12-15
        • 1970-01-01
        • 1970-01-01
        • 2020-12-14
        相关资源
        最近更新 更多