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
相关产品推荐
相关产品推荐

