在Swift中将NSTimeInterval转换为小时、分钟、秒、毫秒。

90

这是我的代码:

func stringFromTimeInterval(interval:NSTimeInterval) -> NSString {

    var ti = NSInteger(interval)
    var ms = ti * 1000
    var seconds = ti % 60
    var minutes = (ti / 60) % 60
    var hours = (ti / 3600)

      return NSString(format: "%0.2d:%0.2d:%0.2d",hours,minutes,seconds,ms)
}

输出结果中毫秒数值不正确,请提供一种正确获取毫秒的方法。


由于NSTimeInterval通常不处理毫秒而是秒,所以我的问题是为什么要乘以“*1000”,并且它应该始终为0。 如果您的实现管理毫秒,则应改为“%1000”,对吗? - Larme
是的,你说得对,它必须是1000%准确的。我希望能够精确计时,所以我使用毫秒单位进行工作。 - Lydia
当然,NSTimeInterval处理秒的分数(例如毫秒)。它是浮点类型,而不是整数。 - Matthias Bauch
@MatthiasBauch,@larme 我的问题是,当我比较两个时间(例如:1:36:22 和 1:36:24,格式为hh:mm:ss)时,它给出的结果是00:00:01而不是00:00:02。因此我认为这可能是毫秒级别的差异导致的。 - Lydia
我也想要针对那个问题的解决方案。 - Lydia
15个回答

180

Swift支持在浮点数上进行余数计算,因此我们可以使用% 1

var ms = Int((interval % 1) * 1000)

就像这样:

func stringFromTimeInterval(interval: TimeInterval) -> NSString {

  let ti = NSInteger(interval)

  let ms = Int((interval % 1) * 1000)

  let seconds = ti % 60
  let minutes = (ti / 60) % 60
  let hours = (ti / 3600)

  return NSString(format: "%0.2d:%0.2d:%0.2d.%0.3d",hours,minutes,seconds,ms)
}

结果:

stringFromTimeInterval(12345.67)                   "03:25:45.670"

Swift 4:

extension TimeInterval{

        func stringFromTimeInterval() -> String {

            let time = NSInteger(self)

            let ms = Int((self.truncatingRemainder(dividingBy: 1)) * 1000)
            let seconds = time % 60
            let minutes = (time / 60) % 60
            let hours = (time / 3600)

            return String(format: "%0.2d:%0.2d:%0.2d.%0.3d",hours,minutes,seconds,ms)

        }
    }

使用:

self.timeLabel.text = player.duration.stringFromTimeInterval()

1
可能需要添加"% 24"来处理小时,例如 "let hours = (ti / 3600) % 24"。 - Gabriel Wamunyu
毫秒数不对,我使用了 ti*1000,否则总是得到0。 - Michał Ziobro
2
“%” 不可用:对于浮点数,请使用 truncatingRemainder。 - JBarros35
这是与地区有关的。在此处查看苹果的单元文档:https://developer.apple.com/videos/play/wwdc2020/10160/ 此响应还具有副作用:TimeInterval 的扩展适用于所有 Double 值,因为 TimeIntervalDouble 的类型别名。以下是文档声明:typealias TimeInterval = Double - a1cd

38

SWIFT 3 扩展

我认为这种方式更容易看出每个部分来自哪里,因此您可以更轻松地根据自己的需求进行修改。

extension TimeInterval {
    private var milliseconds: Int {
        return Int((truncatingRemainder(dividingBy: 1)) * 1000)
    } 

    private var seconds: Int {
        return Int(self) % 60
    } 

    private var minutes: Int {
        return (Int(self) / 60 ) % 60
    } 

    private var hours: Int {
        return Int(self) / 3600
    } 

    var stringTime: String {
        if hours != 0 {
            return "\(hours)h \(minutes)m \(seconds)s"
        } else if minutes != 0 {
            return "\(minutes)m \(seconds)s"
        } else if milliseconds != 0 {
            return "\(seconds)s \(milliseconds)ms"
        } else {
            return "\(seconds)s"
        }
    }
}

2
Jake,这可能是一个很好的答案,但是计算不是100%正确的: | 3599返回-1秒钟| 12601返回3小时29分钟1秒钟。 - Kqtr
4
我已建议一个修正微积分的编辑。再次感谢你的回答! - Kqtr
@PaulS。不知道,可能还没有被批准(吧?) - Kqtr
1
@PaulS。事实证明,我的编辑建议被拒绝了,因为它“偏离了帖子的原意”,但这是链接:https://stackoverflow.com/review/suggested-edits/16940597 - Kqtr
为避免微积分的麻烦,您也可以自己计算余数: interval = interval.rounded() let seconds = Int(interval) - (Int(interval / 60) * 60) - mirap

21

如果小时的零填充不重要,针对iOS 8+、macOS 10.10+的Swift 3解决方案:

func stringFromTime(interval: TimeInterval) -> String {
    let ms = Int(interval.truncatingRemainder(dividingBy: 1) * 1000)
    let formatter = DateComponentsFormatter()
    formatter.allowedUnits = [.hour, .minute, .second]
    return formatter.string(from: interval)! + ".\(ms)"
}

print(stringFromTime(interval: 12345.67)) // "3:25:45.670"

1
如果您想显示零,请使用 formatter.zeroFormattingBehavior = .pad - Sébastien REMY
@iLandes .pad 不会为最高位组件(答案中的小时数)添加前导零。 - vadian
对我而言,只有在 zeroFormattingBehavior = .pad 时才有效。 - idrougge
@idrougge 再次强调,这并不是最重要的组成部分。在这个例子中,即使使用.pad也无法得到“03:25:45.670” - vadian
实际上,如果我在设置allowedUnits之前将添加zeroFormattingBehaviour.pad的行放在前面,它对我起作用。像这样。 formatter.zeroFormattingBehavior = .pad formatter.allowedUnits = [.hour, .minute, .second] - Sanzv
显示剩余2条评论

19

在Objective-C中等价于@matthias-bauch的答案。

+ (NSString *)stringFromTimeInterval:(NSTimeInterval)timeInterval
{
    NSInteger interval = timeInterval;
    NSInteger ms = (fmod(timeInterval, 1) * 1000);
    long seconds = interval % 60;
    long minutes = (interval / 60) % 60;
    long hours = (interval / 3600);

    return [NSString stringWithFormat:@"%0.2ld:%0.2ld:%0.2ld,%0.3ld", hours, minutes, seconds, (long)ms];
}

14

Swift 4:

extension TimeInterval{

        func stringFromTimeInterval() -> String {

            let time = NSInteger(self)

            let ms = Int((self.truncatingRemainder(dividingBy: 1)) * 1000)
            let seconds = time % 60
            let minutes = (time / 60) % 60
            let hours = (time / 3600)

            return String(format: "%0.2d:%0.2d:%0.2d.%0.3d",hours,minutes,seconds,ms)

        }
    }

使用:

self.timeLabel.text = player.duration.stringFromTimeInterval()

Int((self.truncatingRemainder(dividingBy: 1)) * 1000) 总是返回0。 - Michał Ziobro

13

我认为大多数答案都已经过时了,如果您想显示表示时间间隔的字符串,应始终使用DateComponentsFormatter,因为它会为您处理填充和本地化。


4
对于99%的情况而言,这是正确的。当需要使用DateComponentsFormatter不支持的NSCalendar.Unit(例如.nanosecond)时,使用其中一种方法会变得很有用。 - valeCocoa

12

Swift 5. 没有毫秒和一些条件格式(即如果没有小时则不显示小时)。

extension TimeInterval{

func stringFromTimeInterval() -> String {

    let time = NSInteger(self)

    let seconds = time % 60
    let minutes = (time / 60) % 60
    let hours = (time / 3600)

    var formatString = ""
    if hours == 0 {
        if(minutes < 10) {
            formatString = "%2d:%0.2d"
        }else {
            formatString = "%0.2d:%0.2d"
        }
        return String(format: formatString,minutes,seconds)
    }else {
        formatString = "%2d:%0.2d:%0.2d"
        return String(format: formatString,hours,minutes,seconds)
    }
}
}

8

Swift 4,不使用 .remainder(返回错误的值):

func stringFromTimeInterval(interval: Double) -> NSString {

    let hours = (Int(interval) / 3600)
    let minutes = Int(interval / 60) - Int(hours * 60)
    let seconds = Int(interval) - (Int(interval / 60) * 60)

    return NSString(format: "%0.2d:%0.2d:%0.2d",hours,minutes,seconds)
}

4

Swift 4 (带有范围检查 ~ 不会崩溃)

import Foundation

extension TimeInterval {

var stringValue: String {
    guard self > 0 && self < Double.infinity else {
        return "unknown"
    }
    let time = NSInteger(self)

    let ms = Int((self.truncatingRemainder(dividingBy: 1)) * 1000)
    let seconds = time % 60
    let minutes = (time / 60) % 60
    let hours = (time / 3600)

    return String(format: "%0.2d:%0.2d:%0.2d.%0.3d", hours, minutes, seconds, ms)

}
}

TimeInterval可以是负值,这点很好。 - hotdogsoup.nl

4

这是 @hixField 的 Swift 3 版本答案,现在增加了日期和处理之前的日期:

extension TimeInterval {
    func timeIntervalAsString(_ format : String = "dd days, hh hours, mm minutes, ss seconds, sss ms") -> String {
        var asInt   = NSInteger(self)
        let ago = (asInt < 0)
        if (ago) {
            asInt = -asInt
        }
        let ms = Int(self.truncatingRemainder(dividingBy: 1) * (ago ? -1000 : 1000))
        let s = asInt % 60
        let m = (asInt / 60) % 60
        let h = ((asInt / 3600))%24
        let d = (asInt / 86400)

        var value = format
        value = value.replacingOccurrences(of: "hh", with: String(format: "%0.2d", h))
        value = value.replacingOccurrences(of: "mm",  with: String(format: "%0.2d", m))
        value = value.replacingOccurrences(of: "sss", with: String(format: "%0.3d", ms))
        value = value.replacingOccurrences(of: "ss",  with: String(format: "%0.2d", s))
        value = value.replacingOccurrences(of: "dd",  with: String(format: "%d", d))
        if (ago) {
            value += " ago"
        }
        return value
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接