DateIntervalFormatter — 基础,日期间隔格式化

作者: IT Sectr 发布日期: 2026-07-13 阅读时间: 11 分钟

DateIntervalFormatter 是 iOS 和 macOS 中的 Foundation 类,用于将日期范围格式化为本地化字符串:"2024年7月21–25日"、"从2024年7月21日到25日"或"Jul 21–25, 2024"。根据 Apple Developer Documentation, 2024DateIntervalFormatter 自动确定哪些日期组件需要重复(如果范围在同一个月内——月份只显示一次)并本地化分隔符和词序。与手动拼接两个格式化日期不同,此类考虑语言的语法规则:中文使用"到",英语使用不带介词的连字符。此类自 iOS 8 和 macOS 10.10 起可用。

要点

  • DateIntervalFormatter — 用于格式化日期范围的 Foundation 类 (iOS 8+)
  • 自动分组 — 重复组件(月、年)只显示一次
  • 本地化分隔符 — 考虑语法规则:"从–到"(中文),连字符(英语)
  • 三种日期样式 — .none, .short, .medium, .long, .full 用于范围的每一端
  • 四种时间样式 — 范围时间部分相同的详细级别

什么是 DateIntervalFormatter?

DateIntervalFormatter 是 Foundation 中 Formatter 的子类,它接收 DateInterval 对象(两个日期之间的范围)并返回带有此范围描述的本地化字符串。例如,对于2024年7月21日至25日的范围,结果将是中文的"2024年7月21–25日"和英语的"Jul 21–25, 2024"。此类智能地分组重复元素:如果两个日期在同一个月,月份名称只显示一次。

DateIntervalFormatter 的主要价值是自动处理范围的语法。在手动格式化时,开发者需要比较开始和结束的月份/年份,选择分隔符(连字符、"从–到"、"to"、"bis")并监控变格。DateIntervalFormatter 基于 CLDR(通用区域数据仓库)数据自动完成此操作,从而减少国际化应用程序中的错误数量,特别是对于语法复杂的语言。

根据 Unicode CLDR v44 (2024),日期范围格式化规则在不同语言之间差异显著。德语使用介词"bis"(21.–25. Juli 2024),法语使用"au"(du 21 au 25 juillet 2024),日语使用连字符(2024 Jul 21–25)。DateIntervalFormatter 无需开发者编写额外代码即可覆盖所有这些变化。

DateIntervalFormatter 在 Foundation 中如何工作?

工作原理 DateIntervalFormatter 基于对构成范围的两个日期的分析以及考虑 locale 的最佳文本表示的选择。此类比较日期组件(年、月、日)并就分组做出决定:如果年份相同——显示一次;如果月份相同——显示一次;如果天数不同——显示天数范围。

范围zh_CNen_US
一天2024年7月21日Jul 21, 2024
同月内2024年7月21–25日Jul 21–25, 2024
不同月份2024年7月21日 – 8月5日Jul 21 – Aug 5, 2024
不同年份2024年12月25日 – 2025年1月5日Dec 25, 2024 – Jan 5, 2025
带有时间2024年7月21日, 14:30–16:45Jul 21, 2024, 2:30–4:45 PM

分组逻辑 基于 CLDR 模式实现。格式化器按从大到小的顺序检查每个日期组件(Calendar.Component: year, month, day)的相等性。一旦某个组件不同,所有较小的组件都会为两个日期显示。例如,7月21–25日:年份匹配,月份匹配,天数不同 → 月份一次,天数——每个日期分别显示。

重要细微差别: DateIntervalFormatter 不支持通过 API 配置分隔符。分隔符由 locale 确定且无法覆盖。如果需要自定义分隔符(例如,使用"至"代替"–"),您需要通过 DateFormatter 手动格式化日期并使用自定义分隔符连接字符串。

间隔格式化的样式

DateIntervalFormatter 提供两种样式集:dateStyle 和 timeStyle — 用于范围的每一端。这些样式与 DateFormatter 类似:.none 隐藏组件,.short 提供短格式(21.07.2024),.medium — 中等(2024年7月21日),.long — 长格式(2024年7月21日),.full — 最大详细程度(星期日,2024年7月21日)。通过组合 dateStyle 和 timeStyle,可以获得不同的详细级别。

样式组合示例

  • dateStyle = .medium, timeStyle = .none — 仅日期:"2024年7月21–25日"
  • dateStyle = .short, timeStyle = .short — 日期和时间:"21.07.2024, 14:30 – 25.07.2024, 16:45"
  • dateStyle = .none, timeStyle = .short — 仅时间:"14:30–16:45"(如果日期相同)
  • dateStyle = .long, timeStyle = .none — 长日期:"2024年7月21–25日"
  • dateStyle = .full, timeStyle = .short — 完整格式:"星期日,2024年7月21日 – 星期四,2024年7月25日"

重要: dateStyle 和 timeStyle 同等地应用于范围的两端。不能为开始和结束设置不同的样式(例如,开始使用 .medium,结束使用 .short)。如果需要不同的样式,您必须使用单独的 DateFormatter 格式化每个日期并连接结果。

swift
import Foundation

let formatter = DateIntervalFormatter()

// 不同样式组合
let startDate = Date()
let endDate = Date().addingTimeInterval(345600) // +4天
let interval = DateInterval(start: startDate, end: endDate)

// 仅日期,中等格式
formatter.dateStyle = .medium
formatter.timeStyle = .none
print("仅日期:\(formatter.string(from: interval))")

// 带时间的短格式
formatter.dateStyle = .short
formatter.timeStyle = .short
print("带时间的短格式:\(formatter.string(from: interval))")

// 完整格式
formatter.dateStyle = .full
formatter.timeStyle = .none
print("完整日期:\(formatter.string(from: interval))")

Swift 中的 DateIntervalFormatter:示例

基本使用 DateIntervalFormatter 包括创建实例、配置样式和调用 string(from:)。该方法接受 DateInterval — 一个具有 start 和 end 属性的结构。DateInterval 可以从两个 Date 创建,也可以使用 Duration 初始化(iOS 16+)。格式化后,结果即可在 UI 中显示,无需额外处理。

swift
import Foundation

let calendar = Calendar.current
let now = Date()

// 特定间隔示例
let examples: [(title: String, start: Date, end: Date)] = [
    (
        "2-hour event",
        now,
        now.addingTimeInterval(7200)
    ),
    (
        "Week-long vacation",
        now,
        now.addingTimeInterval(604800)
    ),
    (
        "Cross-year period",
        calendar.date(from: DateComponents(
            year: 2024, month: 12, day: 25
        ))!,
        calendar.date(from: DateComponents(
            year: 2025, month: 1, day: 5
        ))!
    )
]

let intervalFormatter = DateIntervalFormatter()
intervalFormatter.dateStyle = .medium
intervalFormatter.timeStyle = .short

for (title, start, end) in examples {
    let interval = DateInterval(start: start, end: end)
    let result = intervalFormatter.string(from: interval)
    print("\(title): \(result)")
}

// 通过 Calendar 格式化 (iOS 16+)
if let nextWeek = calendar.date(
    byAdding: .day, value: 7, to: now
) {
    let weekInterval = DateInterval(start: now, end: nextWeek)
    print("接下来7天:\(intervalFormatter.string(from: weekInterval))")
}

DateIntervalFormatter 和 Calendar Booking: 在预订应用(酒店、门票)中,日期范围是 UI 的关键元素。DateIntervalFormatter 正确处理跨年范围(例如,从12月28日到1月3日),为每个日期分别分组月份和年份。对于此类场景,建议使用 dateStyle = .medium 和 timeStyle = .none — 对于没有时间的日期,这是最可读的格式。

swift
import Foundation

let formatter = DateIntervalFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none

// 预订示例
struct Booking {
    let checkIn: Date
    let checkOut: Date

    var formattedInterval: String {
        let interval = DateInterval(start: checkIn, end: checkOut)
        return formatter.string(from: interval)
    }
}

let booking = Booking(
    checkIn: calendar.date(from: DateComponents(
        year: 2024, month: 7, day: 21
    ))!,
    checkOut: calendar.date(from: DateComponents(
        year: 2024, month: 7, day: 25
    ))!
)
print("预订:\(booking.formattedInterval)")

// 使用不同区域设置
let enFormatter = DateIntervalFormatter()
enFormatter.dateStyle = .medium
enFormatter.timeStyle = .none
enFormatter.locale = Locale(identifier: "en_US")
print("英语:\(enFormatter.string(from: interval))")

DateInterval 与 DateIntervalFormatter: DateInterval — 表示日期范围的结构(开始和结束)。DateIntervalFormatter — 将此结构转换为字符串的格式化器。DateInterval 还支持 contains(Date) 和 intersection(with:) 检查,这对于预订逻辑(检查日期重叠)很有用。

日期间隔的本地化

DateIntervalFormatter 通过 locale 属性本地化输出。默认使用 Locale.current — 设备的区域设置。要以其他语言显示间隔(例如,中文界面中的英文内容),请显式设置 locale。与 DateFormatter 不同,DateIntervalFormatter 不需要为服务器数据设置 Locale — 它始终使用自己基于 CLDR 的内部分组逻辑。

日期范围格式化的语言差异比单个日期复杂得多。英语使用简单连字符:Jul 21–25。中文使用"从–到":从7月21日到25日。法语使用"au":du 21 au 25 juillet。德语使用数字后加点加连字符:21.–25. Juli。日语使用不带介词的连字符:Jul 21–25。DateIntervalFormatter 覆盖所有这些变体。

swift
import Foundation

let formatter = DateIntervalFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none

let start = Date()
let end = Date().addingTimeInterval(259200) // +3天
let interval = DateInterval(start: start, end: end)

// 展示不同区域设置
let localeIds = ["ru_RU", "en_US", "de_DE", "fr_FR", "ja_JP", "zh_CN"]
for localeId in localeIds {
    formatter.locale = Locale(identifier: localeId)
    print("\(localeId): \(formatter.string(from: interval))")
}

// 时间间隔的本地化
formatter.dateStyle = .none
formatter.timeStyle = .short

let todayStart = Date()
let todayEnd = Date().addingTimeInterval(3600)
let timeInterval = DateInterval(start: todayStart, end: todayEnd)

for localeId in ["ru_RU", "en_US", "de_DE"] {
    formatter.locale = Locale(identifier: localeId)
    print("\(localeId)时间:\(formatter.string(from: timeInterval))")
}

本地化特点: 对于中文,DateIntervalFormatter 在长日期格式中使用"从"和"到"介词。对于短格式 (short) — 仅连字符:21.07–25.07.2024。对于完整格式 (full) — 带有星期几。格式的选择取决于上下文:对于预订列表使用 .medium,对于详细视图使用 .long 或 .full。

与其他日期格式化器的比较

DateIntervalFormatter 填补了其他 Foundation 类未覆盖的日期范围格式化的领域。DateFormatter 格式化单个日期,RelativeDateTimeFormatter 格式化相对日期(之前/之后),DateComponentsFormatter 格式化持续时间(2小时30分钟)。这些类各自解决自己的任务,不能互换使用。

用途输出示例
DateIntervalFormatter日期范围2024年7月21–25日
DateFormatter单个日期2024年7月21日
RelativeDateTimeFormatter相对时间3天前
DateComponentsFormatter持续时间2小时30分钟
ISO8601DateFormatterISO 8601 格式2024-07-21T14:30:00Z

何时使用 DateIntervalFormatter: 用于在 UI 中显示日期范围 — 酒店预订、活动、按日期筛选、操作历史。要计算日期之间的持续时间,请使用 DateComponentsFormatter,而不是 DateIntervalFormatter — 后者不用于显示持续时间。DateFormatter 应仅用于单个日期,而不是通过字符串拼接来连接两个格式化日期 — 这会导致分隔符本地化的丢失。

边缘情况: DateIntervalFormatter 不支持显示开放范围(无结束日期)。如果范围结束未知(例如,"从7月21日起"),对于中文请使用带有"从"介词的 DateFormatter。DateIntervalFormatter 也不支持为不同日期指定时间的范围 — 时间分别应用于每个日期,对于短范围这可能显得多余。

常见问题

什么是 DateIntervalFormatter?

DateIntervalFormatter — 用于将日期范围格式化为本地化字符串的 Foundation 类。自 iOS 8 起可用,自动分组重复组件(月、年)并为语言选择正确的分隔符。

DateIntervalFormatter 如何分组日期?

比较组件 从年到日。如果年份匹配 — 显示一次。如果月份匹配 — 显示一次。不同的天显示为范围。这给出"2024年7月21–25日"而不是"2024年7月21日 – 2024年7月25日"。

可以更改范围分隔符吗?

不能,分隔符由 locale 确定且不能通过 API 配置。对于自定义分隔符(例如,使用"至"代替"–"),使用单独的 DateFormatter 格式化每个日期并手动连接字符串。

DateIntervalFormatter 与 DateFormatter 有何不同?

DateIntervalFormatter 将日期范围(两个日期)格式化为一个带有组件分组的整体。DateFormatter 格式化单个日期。对于范围,始终使用 DateIntervalFormatter — 这能确保分隔符的正确本地化。

如何格式化不同年份的范围?

自动 — DateIntervalFormatter 检测到年份不同并为两端显示完整日期:"2024年12月25日 – 2025年1月5日"。无需额外设置,只需设置 dateStyle 即可。

总结

  • DateIntervalFormatter — 用于格式化日期范围的专业类 (iOS 8+)
  • 自动分组 — 重复组件(年、月)为整个范围显示一次
  • 本地化分隔符 — "从–到"(中文)、连字符(英语)、"bis"(德语)— 自动
  • 日期和时间样式 — .none、.short、.medium、.long、.full 组合用于范围的每一端
  • 不适用于持续时间 — 要计算持续时间,请使用 DateComponentsFormatter
  • 不支持自定义分隔符 — 对于非标准情况,需要手动格式化
  • 与 DateInterval 集成 — DateInterval 结构提供 contains 和 intersection 检查

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读