Date — 是一个 Foundation 结构体,表示独立于日历和时区的特定时间点。该类型存储自参考日期 2001 年 1 月 1 日(reference date)以来经过的秒数,并提供基本的比较和时间间隔计算操作。根据 Apple Developer Documentation (2025),Date 在所有 Apple 框架中使用:从 UIKit 和 SwiftUI 到 CloudKit 和 Core Data。理解 Date 的工作原理对每个 iOS 开发者都是必须的,因为没有它就无法在应用程序中管理时间。
要点
Date — 是 Foundation 框架中的一个值类型,用于记录一个绝对时间点。与日历表示(年、月、日)不同,Date 不依赖于时区和日历系统 — 它是纯粹数值的时间戳值。
Date 的内部表示是一个 TimeInterval(Double 的别名),存储相对于参考日期 — 2001 年 1 月 1 日 00:00:00 UTC 的秒数。Apple 选择了这个日期而不是 Unix 纪元(1970 年),是为了与构建 iOS 和 macOS 所基于的旧 NeXTSTEP 系统兼容。
Date 在所有 Apple API 中用作标准时间类型:UIDatePicker、DatePicker、UserDefaults、Codable、Core Data、CloudKit。没有 Date,就无法处理日程、计时器、日历事件、数据序列化以及任何与时间相关的操作。
Date 与日历类型不同,因为它表示物理时间戳,而不是人类可读的值。同一个 Date 在不同的时区可能对应不同的日历日期。要获取组件(年、月、日),Date 通过 Calendar 和 DateComponents 进行转换。
Date 也不同于 DispatchTime,后者用于 Grand Central Dispatch 中的任务调度。DispatchTime 相对于系统启动的时刻,而 Date 是可以保存、传输和恢复的绝对时间戳。
Date() — 是创建包含当前时间点对象的最简单方法。无参数的初始化器返回调用的确切时间。要创建过去或未来的日期,使用 TimeInterval 和 addingTimeInterval 方法,或从现有的 Date 进行初始化。
要从组件创建任意日期,使用 Calendar 和 DateComponents 配对。Calendar 将人类可读的组件(年、月、日、时、分)转换为 Date。也可以通过 ISO8601DateFormatter 从 ISO 8601 格式的字符串初始化 Date,或通过 DateFormatter 从任意字符串表示初始化 Date。
Date.distantPast 和 Date.distantFuture — 两个静态属性,表示过去和未来无限遥远的时刻。它们用作算法中日期过滤和排序的边界值。
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 — 是 Double 的别名,表示两个时间点之间的秒数。TimeInterval 的精度达到毫秒级,足以满足大多数应用场景。负的 TimeInterval 值表示相对于参考点的过去。
Date 支持所有标准比较运算符:==、<、>、<=、>=。Swift 自动比较值的内部 TimeInterval。compare 方法返回 ComparisonResult,带有 .orderedSame、.orderedAscending、.orderedDescending 变体,用于更明确的比较。
Date.distance(to:) — 返回到指定日期的 TimeInterval 的方法。如果目标日期在过去,结果为负。advanced(by:) 通过将指定的 TimeInterval 添加到当前日期来创建新日期。这些方法实现了 Strideable 协议,允许 Date 在 Range 和 ClosedRange 范围中使用。
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 — 是 Date 和人类可读组件之间的桥梁。Calendar 将 Date 转换为 DateComponents(年、月、日、时、分、秒),考虑时区和区域设置。没有 Calendar,就无法从 Date 中提取年或月 — Date 由于其绝对性质不包含这些信息。
Calendar.component 方法从 Date 中提取一个组件:年、月、日、时、分、秒、weekday、quarter。Calendar.dateComponents 方法在一次调用中提取一组组件 — 这比为每个字段单独调用 component 更高效。
Calendar.isDateInToday、isDateInTomorrow、isDateInYesterday — 用于检查相对日期的便捷方法。Calendar.dateInterval 返回指定期间(日、周、月、年)的开始和结束。Calendar.compare 以指定组件的精度比较日期 — 例如,年份而不考虑月和日。
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 — 如果需要向用户显示 Date,则是必需组件。DateFormatter 根据指定的格式和区域设置将 Date 转换为字符串。通过 String(describing:) 直接将 Date 转换为字符串会返回带有 reference date 和 TimeInterval 的技术表示 — 这不是为用户准备的。
基本方法是使用预定义的样式 dateStyle 和 timeStyle。iOS 支持四种样式:.short、.medium、.long、.full。日期和时间样式的组合允许灵活调整显示。DateFormatter 自动应用本地化:对于中文区域设置,日期将以 \"2026年7月21日\" 格式显示。
ISO8601DateFormatter — 专用于 ISO 8601 的格式化器,ISO 8601 是 API 和数据交换最常用的格式。这个格式化器比 DateFormatter 工作得更快,且不依赖于区域设置,使其成为 JSON 中日期序列化的首选。
// 预定义样式
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\")。
让我们看看 iOS 应用程序中使用 Date 的典型场景。第一个场景 — 从出生日期 计算年龄。第二个 — 到事件的 倒计时计时器。第三个 — 按日期对记录进行分组,以表格部分的形式显示。
Calendar.dateComponents 带有 [.year] 标志计算两个日期之间的差异。设置正确的时区至关重要 — 如果在 Calendar 中不指定 timeZone,结果可能因为跨越另一个时区的午夜而偏差一天。
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.timeIntervalSince | Timer+Combine |
| 日期分组 | Calendar.dateInterval | 规范化为天 |
| JSON 序列化 | ISO8601DateFormatter | Locale 不影响 |
常见问题
Date — 是时间的内部表示,不关联日历和区域设置。字符串是格式化的显示,依赖于用户设置。Date 可以以百分之百的精度进行比较、序列化和数学处理,而字符串表示总需要通过格式化器进行解析。
Date — 是绝对时间戳(从 reference date 开始的秒数),不依赖于时区。时区是显示的上下文。同一个 Date 在 UTC 和北京代表相同的物理时刻,但显示为不同的日历时间。
Date 被 Core Data 直接支持作为属性类型。Core Data 自动将 Date 序列化到存储中。对于按日期过滤的查询,使用带有比较运算符的 NSPredicate — Core Data 在 SQLite 级别正确处理时区。
CFAbsoluteTime — 是 Core Foundation 类型,使用相同的参考日期(2001 年 1 月 1 日)。Date 和 CFAbsoluteTime 可以通过直接转换互换。Date(timeIntervalSinceReferenceDate:) 接受 CFAbsoluteTime,Date.timeIntervalSinceReferenceDate 返回 CFAbsoluteTime。
Date 具有毫秒级精度,但由于 iOS/macOS 的硬件限制,不保证纳秒级精度。对于高精度测量,使用 mach_absolute_time 或 Swift 5.7+ 中的 ContinuousClock,它们基于硬件处理器周期计数器工作。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。