Swift 中日期和时间的高级处理
日期和时间处理是 iOS/macOS 开发中的关键任务之一。在基础层面上,大多数开发者都熟悉 Date、DateFormatter 和 Calendar。然而,在高级层面上会出现复杂的场景:时区处理、解析数百个日期时的高性能要求、相对时间的正确显示以及自定义格式的处理。在本文中,我们将深入探讨 Swift 中日期处理的高级技巧,帮助您编写更可靠、更高效的代码。
1. 日期解析优化:DateFormatter vs ISO8601DateFormatter
最常见的操作之一是将字符串转换为日期。标准的 DateFormatter 使用方便,但创建它的成本很高。如果您在循环中解析数百个日期,每次调用 DateFormatter() 都会创建一个新对象,从而导致性能下降。
问题:在循环中创建 DateFormatter
// ❌ 不好:在循环中创建格式化器for dateString in dateStrings { let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ" if let date = formatter.date(from: dateString) { // 处理 }}解决方案:缓存和 ISO8601DateFormatter
对于 ISO 8601 格式,请使用 ISO8601DateFormatter——它更快且线程安全。对于自定义格式,请创建一次格式化器并重复使用。
// ✅ 好:缓存格式化器private let isoFormatter: ISO8601DateFormatter = { let formatter = ISO8601DateFormatter() formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] return formatter}()
// 对于自定义格式private let customFormatter: DateFormatter = { let formatter = DateFormatter() formatter.dateFormat = "dd.MM.yyyy HH:mm" formatter.locale = Locale(identifier: "ru_RU") formatter.timeZone = TimeZone(secondsFromGMT: 0) return formatter}()
func parseDates(_ strings: [String]) -> [Date] { return strings.compactMap { isoFormatter.date(from: $0) }}重要提示: DateFormatter 不是线程安全的。如果您处理多线程,请使用 Thread.current 或为每个线程创建单独的格式化器。
2. 时区和 DateComponents 的处理
高级日期处理需要考虑时区。Swift 中的 Date 是一个绝对时间点(自 2001 年 1 月 1 日以来的秒数)。所有时区信息都存在于 Calendar 和 DateFormatter 中。
在特定时区中提取日期组件
let date = Date()let calendar = Calendar.currentlet timeZone = TimeZone(identifier: "America/New_York")!
// 获取指定时区的组件var components = calendar.dateComponents(in: timeZone, from: date)components.hour // 纽约的小时components.day // 纽约的日期
// 从组件创建日期并显式指定时区var newComponents = DateComponents()newComponents.year = 2024newComponents.month = 12newComponents.day = 25newComponents.hour = 10newComponents.minute = 30newComponents.timeZone = TimeZone(identifier: "Europe/Moscow")
let christmasDate = calendar.date(from: newComponents)print(christmasDate!) // 2024-12-25 07:30:00 +0000 (UTC)考虑日历的日期比较
使用 compare(_:to:toGranularity:) 进行精确到天、小时或分钟的比较:
let date1 = Date()let date2 = Date().addingTimeInterval(3600) // +1 小时
let result = calendar.compare(date1, to: date2, toGranularity: .hour)// .orderedSame,如果在同一小时内// .orderedAscending,如果 date1 更早