You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用DateComponentsFormatter计算日期差无法获取毫秒值

解决DateComponentsFormatter无法显示毫秒的问题

我懂你遇到的困扰——DateComponentsFormatter确实没办法直接帮你输出毫秒级的倒计时,原因很简单:它没有专门的「毫秒」单位选项,而且你设置的.nanosecond只会显示成纳秒(数值会是很大的数字,比如1毫秒=1e6纳秒),根本不符合你想要的“X毫秒”格式。

下面给你两种可行的解决方案,按需选择:

方案一:基于DateComponentsFormatter扩展,拼接毫秒部分

这个方案保留Formatter处理年、月、日等大单位的便捷性,只手动补充毫秒部分:

extension DateComponentsFormatter {
    func differenceWithMilliseconds(from fromDate: Date, to toDate: Date) -> String? {
        // 先让Formatter处理到秒的部分,忽略纳秒
        self.allowedUnits = [.year, .month, .day, .hour, .minute, .second]
        self.maximumUnitCount = 6
        self.unitsStyle = .full
        self.zeroFormattingBehavior = .dropAll // 自动隐藏值为0的单位
        
        guard let secondsPart = self.string(from: fromDate, to: toDate) else {
            return nil
        }
        
        // 计算两个日期之间的毫秒差:取时间间隔的小数部分乘以1000
        let timeInterval = toDate.timeIntervalSince(fromDate)
        let milliseconds = Int((timeInterval.truncatingRemainder(dividingBy: 1)) * 1000)
        
        // 拼接成最终的倒计时字符串
        return "\(secondsPart) \(milliseconds)毫秒"
    }
}

使用示例:

let formatter = DateComponentsFormatter()
let now = Date()
let nextGame = now.addingTimeInterval(365*24*3600 + 2*30*24*3600 + 6*24*3600 + 23*60 +24 + 0.005)
if let countdown = formatter.differenceWithMilliseconds(from: now, to: nextGame) {
    print("距离下一场比赛还有\(countdown)")
    // 输出:距离下一场比赛还有1年2个月6天23分钟24秒 5毫秒
}

方案二:完全自定义格式化逻辑(精确控制所有单位)

如果你想更灵活地控制显示格式(比如隐藏值为0的单位、自定义单位名称),可以直接通过Calendar计算日期组件,再手动计算毫秒:

func preciseCountdownString(from fromDate: Date, to toDate: Date) -> String {
    guard toDate > fromDate else {
        return "比赛已开始"
    }
    
    let calendar = Calendar.current
    // 获取精确的年、月、日、时、分、秒组件
    let components = calendar.dateComponents(
        [.year, .month, .day, .hour, .minute, .second],
        from: fromDate,
        to: toDate
    )
    
    // 计算毫秒部分
    let timeInterval = toDate.timeIntervalSince(fromDate)
    let milliseconds = Int((timeInterval.truncatingRemainder(dividingBy: 1)) * 1000)
    
    // 组装所有需要显示的单位
    var parts = [String]()
    if let year = components.year, year > 0 {
        parts.append("\(year)年")
    }
    if let month = components.month, month > 0 {
        parts.append("\(month)个月")
    }
    if let day = components.day, day > 0 {
        parts.append("\(day)天")
    }
    if let hour = components.hour, hour > 0 {
        parts.append("\(hour)小时")
    }
    if let minute = components.minute, minute > 0 {
        parts.append("\(minute)分")
    }
    if let second = components.second, second > 0 {
        parts.append("\(second)秒")
    }
    parts.append("\(milliseconds)毫秒")
    
    return "距离下一场比赛还有" + parts.joined(separator: " ")
}

这个方案的优势是:用Calendar计算的月、日都是精确符合实际日历的(不会像用固定30天算月份那样有误差),而且你可以完全控制哪些单位显示、怎么显示。

为什么你的原代码不行?

你原代码里设置了.nanosecond,但DateComponentsFormatter的.full单位样式会把它显示为“纳秒”,而且1毫秒等于1,000,000纳秒,所以显示出来的数值会非常大,完全不是你想要的毫秒格式。另外,Formatter默认会隐藏值为0的单位,但如果前面的单位已经占满了maximumUnitCount,小单位也可能被截断,导致纳秒根本不显示。

内容的提问来源于stack exchange,提问作者Govind Rakholiya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 03:28:44