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

Swift中如何存储并格式化以天、周、月为单位的时长量

这个问题我之前也碰到过,系统自带的Duration和Measurement<UnitDuration>确实不支持直接用天、周、月作为单位,而且自定义UnitDuration后单复数格式化也会出问题。这里有几个实用的解决方案,能完美实现你想要的效果:

解决方案一:扩展Duration+自定义格式化方法

首先给Duration添加便捷初始化方法,让你能像.days(1)这样创建时长,然后自定义格式化逻辑处理单复数:

extension Duration {
    // 便捷初始化:天、周、月(月按30天估算,可根据需求调整)
    static func days(_ value: Int) -> Duration {
        .seconds(value * 24 * 60 * 60)
    }
    
    static func weeks(_ value: Int) -> Duration {
        .days(value * 7)
    }
    
    static func months(_ value: Int) -> Duration {
        .days(value * 30)
    }
    
    // 自定义格式化方法,支持指定单位或自动选择最优单位
    func formattedNatural(unit: TimeUnit? = nil) -> String {
        enum TimeUnit { case days, weeks, months }
        
        let totalSeconds = components.seconds + components.nanoseconds / 1_000_000_000
        let totalDays = totalSeconds / (24 * 60 * 60)
        
        // 确定要使用的单位和对应数值
        let (value, unitStr): (Int, String)
        if let specifiedUnit = unit {
            switch specifiedUnit {
            case .days:
                value = totalDays
                unitStr = value == 1 ? "day" : "days"
            case .weeks:
                value = totalDays / 7
                unitStr = value == 1 ? "week" : "weeks"
            case .months:
                value = totalDays / 30
                unitStr = value == 1 ? "month" : "months"
            }
        } else {
            // 自动选择最合适的单位
            switch totalDays {
            case 0..<7:
                value = totalDays
                unitStr = value == 1 ? "day" : "days"
            case 7..<30:
                value = totalDays / 7
                unitStr = value == 1 ? "week" : "weeks"
            default:
                value = totalDays / 30
                unitStr = value == 1 ? "month" : "months"
            }
        }
        
        return "\(value) \(unitStr)"
    }
}

使用示例:

let oneDay = Duration.days(1)
let eightWeeks = Duration.weeks(8)
let twoMonths = Duration.months(2)

print(oneDay.formattedNatural()) // 输出:"1 day"
print(eightWeeks.formattedNatural()) // 输出:"8 weeks"
print(twoMonths.formattedNatural()) // 输出:"2 months"

// 强制指定单位输出
print(eightWeeks.formattedNatural(unit: .days)) // 输出:"56 days"
解决方案二:自定义DurationFormatter类(更灵活复用)

如果需要在多个地方复用格式化逻辑,可以单独写一个Formatter类,这样符合iOS的格式化规范:

class DurationFormatter: Formatter {
    enum PreferredUnit { case days, weeks, months }
    var preferredUnit: PreferredUnit = .days
    
    override func string(for obj: Any?) -> String? {
        guard let duration = obj as? Duration else { return nil }
        
        let totalSeconds = duration.components.seconds + duration.components.nanoseconds / 1_000_000_000
        let totalDays = totalSeconds / (24 * 60 * 60)
        
        switch preferredUnit {
        case .days:
            let value = totalDays
            return "\(value) \(value == 1 ? "day" : "days")"
        case .weeks:
            let value = totalDays / 7
            return "\(value) \(value == 1 ? "week" : "weeks")"
        case .months:
            let value = totalDays / 30
            return "\(value) \(value == 1 ? "month" : "months")"
        }
    }
}

使用示例:

let formatter = DurationFormatter()
let oneDay = Duration.days(1)
let eightWeeks = Duration.weeks(8)

formatter.preferredUnit = .days
print(formatter.string(for: oneDay)!) // "1 day"

formatter.preferredUnit = .weeks
print(formatter.string(for: eightWeeks)!) // "8 weeks"
为什么扩展UnitDuration会出现单复数问题?

你之前尝试扩展UnitDuration时,自定义的.days单位没有系统自带单位的本地化支持。系统的UnitDuration单位(比如.hours)在格式化时会读取系统本地化资源里的单复数字符串,而自定义单位没有对应的资源,所以只能显示你设置的固定symbol,导致1 days这种错误。自定义Formatter的方式更直接可控,不需要修改本地化文件。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 16:27:41