Date:是什么,Foundation 中的时间类型和 TimeInterval

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

Date — 是一个 Foundation 结构体,表示独立于日历和时区的特定时间点。该类型存储自参考日期 2001 年 1 月 1 日(reference date)以来经过的秒数,并提供基本的比较和时间间隔计算操作。根据 Apple Developer Documentation (2025),Date 在所有 Apple 框架中使用:从 UIKit 和 SwiftUI 到 CloudKit 和 Core Data。理解 Date 的工作原理对每个 iOS 开发者都是必须的,因为没有它就无法在应用程序中管理时间。

要点

  • Date — Foundation 结构体,将绝对时间点存储为从 reference date 开始的秒数。
  • TimeInterval — Double 类型,用于以秒为单位表示两个时间点之间的间隔。
  • Date() — 初始化器,在调用时创建包含当前时间点的对象。
  • 日期比较 通过比较运算符或 compare、earlierDate、laterDate 方法执行。
  • DateFormatter — 以可读格式向用户显示日期的必需组件。

Foundation 中的 Date 是什么?

Date — 是 Foundation 框架中的一个值类型,用于记录一个绝对时间点。与日历表示(年、月、日)不同,Date 不依赖于时区和日历系统 — 它是纯粹数值的时间戳值。

Date 的内部表示是一个 TimeInterval(Double 的别名),存储相对于参考日期 — 2001 年 1 月 1 日 00:00:00 UTC 的秒数。Apple 选择了这个日期而不是 Unix 纪元(1970 年),是为了与构建 iOS 和 macOS 所基于的旧 NeXTSTEP 系统兼容。

Date 在所有 Apple API 中用作标准时间类型:UIDatePickerDatePickerUserDefaultsCodableCore DataCloudKit。没有 Date,就无法处理日程、计时器、日历事件、数据序列化以及任何与时间相关的操作。

Date 与其他时间类型的比较

Date 与日历类型不同,因为它表示物理时间戳,而不是人类可读的值。同一个 Date 在不同的时区可能对应不同的日历日期。要获取组件(年、月、日),Date 通过 CalendarDateComponents 进行转换。

Date 也不同于 DispatchTime,后者用于 Grand Central Dispatch 中的任务调度。DispatchTime 相对于系统启动的时刻,而 Date 是可以保存、传输和恢复的绝对时间戳。

在 Swift 中创建 Date

Date() — 是创建包含当前时间点对象的最简单方法。无参数的初始化器返回调用的确切时间。要创建过去或未来的日期,使用 TimeInterval 和 addingTimeInterval 方法,或从现有的 Date 进行初始化。

要从组件创建任意日期,使用 CalendarDateComponents 配对。Calendar 将人类可读的组件(年、月、日、时、分)转换为 Date。也可以通过 ISO8601DateFormatter 从 ISO 8601 格式的字符串初始化 Date,或通过 DateFormatter 从任意字符串表示初始化 Date。

Date.distantPastDate.distantFuture — 两个静态属性,表示过去和未来无限遥远的时刻。它们用作算法中日期过滤和排序的边界值。

swift
import Foundation

// 使用当前时间创建 Date
let now = Date()
print("Current time: \(now)")

// 1 小时后的日期(3600 秒)
let inOneHour = now.addingTimeInterval(3600)

// 从组件创建日期
var components = DateComponents()
components.year = 2025
components.month = 12
components.day = 25
let christmas = Calendar.current.date(from: components)!

示例中展示了三种创建 Date 的方法:当前时间、添加间隔和通过 DateComponents 创建。所有三种方法都返回 Date 类型的值,精度达到毫秒。Calendar.current 使用用户的系统日历 — 在 Apple 设备上通常是公历。

TimeInterval 和日期比较

TimeInterval — 是 Double 的别名,表示两个时间点之间的秒数。TimeInterval 的精度达到毫秒级,足以满足大多数应用场景。负的 TimeInterval 值表示相对于参考点的过去。

Date 支持所有标准比较运算符:==<><=>=。Swift 自动比较值的内部 TimeInterval。compare 方法返回 ComparisonResult,带有 .orderedSame、.orderedAscending、.orderedDescending 变体,用于更明确的比较。

Date.distance(to:) — 返回到指定日期的 TimeInterval 的方法。如果目标日期在过去,结果为负。advanced(by:) 通过将指定的 TimeInterval 添加到当前日期来创建新日期。这些方法实现了 Strideable 协议,允许 Date 在 RangeClosedRange 范围中使用。

swift
let start = Date()
let end = start.addingTimeInterval(86400)

// 通过运算符比较
if start < end {
    print("start is earlier")
}

// 两个日期之间的 TimeInterval
let interval: TimeInterval = end.timeIntervalSince(start)
print("Seconds: \(Int(interval / 60)) minutes")

// Using Date in range
let range = start...end
let mid = start.addingTimeInterval(43200)
if range.contains(mid) {
    print("mid is between start and end")
}

等于 86400 的 TimeInterval 对应一天。对于更复杂的间隔 — 周、月、年 — 使用 Calendar 和 DateComponents,因为这些单位的长度是可变的(闰年、夏令时切换)。TimeInterval 仅适用于以秒为单位的固定间隔。

Calendar 和 DateComponents

Calendar — 是 Date 和人类可读组件之间的桥梁。Calendar 将 Date 转换为 DateComponents(年、月、日、时、分、秒),考虑时区和区域设置。没有 Calendar,就无法从 Date 中提取年或月 — Date 由于其绝对性质不包含这些信息。

Calendar.component 方法从 Date 中提取一个组件:年、月、日、时、分、秒、weekday、quarter。Calendar.dateComponents 方法在一次调用中提取一组组件 — 这比为每个字段单独调用 component 更高效。

Calendar.isDateInTodayisDateInTomorrowisDateInYesterday — 用于检查相对日期的便捷方法。Calendar.dateInterval 返回指定期间(日、周、月、年)的开始和结束。Calendar.compare 以指定组件的精度比较日期 — 例如,年份而不考虑月和日。

swift
let calendar = Calendar.current

// 提取组件
let year = calendar.component(.year, from: Date())
let month = calendar.component(.month, from: Date())

// Check relative date
if calendar.isDateInToday(someDate) {
    print("Today!")
}

// 一天的开始和结束
let dayInterval = calendar.dateInterval(of: .day, for: Date())!
print("Day start: \(dayInterval.start)")
print("Day end: \(dayInterval.end)")

Calendar.current 使用用户的系统设置 — 日历、时区和区域设置。对于测试或特定计算,可以通过 Calendar(identifier: .gregorian) 创建具有具体参数的 Calendar,并通过 timeZone 属性设置所需的 TimeZone。

DateFormatter 和日期显示

DateFormatter — 如果需要向用户显示 Date,则是必需组件。DateFormatter 根据指定的格式和区域设置将 Date 转换为字符串。通过 String(describing:) 直接将 Date 转换为字符串会返回带有 reference date 和 TimeInterval 的技术表示 — 这不是为用户准备的。

基本方法是使用预定义的样式 dateStyletimeStyle。iOS 支持四种样式:.short、.medium、.long、.full。日期和时间样式的组合允许灵活调整显示。DateFormatter 自动应用本地化:对于中文区域设置,日期将以 \"2026年7月21日\" 格式显示。

ISO8601DateFormatter — 专用于 ISO 8601 的格式化器,ISO 8601 是 API 和数据交换最常用的格式。这个格式化器比 DateFormatter 工作得更快,且不依赖于区域设置,使其成为 JSON 中日期序列化的首选。

swift
// 预定义样式
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
let userString = formatter.string(from: Date())

// 用于 API 的 ISO 8601
let isoFormatter = ISO8601DateFormatter()
let apiString = isoFormatter.string(from: Date())

// 从字符串解析
let parsed = isoFormatter.date(from: "2026-07-21T10:30:00Z")!

在使用 DateFormatter 时,重要的是要记住 性能:创建新的格式化器是一项昂贵的操作。建议缓存格式化器或创建一次并重复使用。在格式化器的属性中可以设置 timeZone — 默认使用系统时区,但对于服务器日期,通常需要 TimeZone(identifier: \"UTC\")。

Date 使用示例

让我们看看 iOS 应用程序中使用 Date 的典型场景。第一个场景 — 从出生日期 计算年龄。第二个 — 到事件的 倒计时计时器。第三个 — 按日期对记录进行分组,以表格部分的形式显示。

从出生日期计算年龄

Calendar.dateComponents 带有 [.year] 标志计算两个日期之间的差异。设置正确的时区至关重要 — 如果在 Calendar 中不指定 timeZone,结果可能因为跨越另一个时区的午夜而偏差一天。

swift
func calculateAge(from birthDate: Date) -> Int {
    let calendar = Calendar.current
    let now = Date()
    let components = calendar.dateComponents(
        [.year], from: birthDate, to: now
    )
    return components.year ?? 0
}

倒计时计时器 通过 TimeInterval 实现。我们计算目标日期和当前日期之间的差异,得到剩余的秒数,并转换为天、小时、分钟。为了定期更新,使用间隔为 1 秒的 Timer。带有 @Published 属性的 ObservableObject 允许 SwiftUI 自动更新界面。

按日期分组 通过以 Date 为键的字典实现。Calendar.dateInterval(of: .day, for:) 将日期规范化为一天的开始,从而可以对属于同一天的记录进行分组。同样的方法适用于周、月和年 — 只需将 dateInterval 中的组件更改为 .weekOfYear 或 .month 即可。

场景关键 API特点
计算年龄Calendar.dateComponents考虑 timeZone
倒计时Date.timeIntervalSinceTimer+Combine
日期分组Calendar.dateInterval规范化为天
JSON 序列化ISO8601DateFormatterLocale 不影响

常见问题

Date 与日期字符串有什么区别?

Date — 是时间的内部表示,不关联日历和区域设置。字符串是格式化的显示,依赖于用户设置。Date 可以以百分之百的精度进行比较、序列化和数学处理,而字符串表示总需要通过格式化器进行解析。

为什么 Date 不包含时区信息?

Date — 是绝对时间戳(从 reference date 开始的秒数),不依赖于时区。时区是显示的上下文。同一个 Date 在 UTC 和北京代表相同的物理时刻,但显示为不同的日历时间。

在 Core Data 中存储时间应该使用什么类型?

Date 被 Core Data 直接支持作为属性类型。Core Data 自动将 Date 序列化到存储中。对于按日期过滤的查询,使用带有比较运算符的 NSPredicate — Core Data 在 SQLite 级别正确处理时区。

Date 和 CFAbsoluteTime 有什么区别?

CFAbsoluteTime — 是 Core Foundation 类型,使用相同的参考日期(2001 年 1 月 1 日)。Date 和 CFAbsoluteTime 可以通过直接转换互换。Date(timeIntervalSinceReferenceDate:) 接受 CFAbsoluteTime,Date.timeIntervalSinceReferenceDate 返回 CFAbsoluteTime。

Date 可以用于高精度测量吗?

Date 具有毫秒级精度,但由于 iOS/macOS 的硬件限制,不保证纳秒级精度。对于高精度测量,使用 mach_absolute_time 或 Swift 5.7+ 中的 ContinuousClock,它们基于硬件处理器周期计数器工作。

总结

  • Date — Foundation 值类型,用于将绝对时间点存储为从 2001 年 1 月 1 日 reference date 开始的 TimeInterval。
  • TimeInterval (Double) — 两个日期之间的秒数,提供 Date 的比较和算术运算。
  • CalendarDateComponents — 从 Date 中提取年、月、日和其他日历组件的唯一方法。
  • DateFormatterISO8601DateFormatter — 以文本格式显示和序列化日期的必需工具。
  • Date.distantPastDate.distantFuture — 算法中过滤和排序的边界值。
  • Date 不包含 timezone — 时区在提取组件以显示时在 Calendar 级别添加。
  • Core Data 和 Codable 原生支持 Date,简化了日期的存储和传输,无需额外转换。

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

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

讨论项目

另请阅读