当我们想到日期或者时间的计算时,一般会认为比较简单,我们都知道:1分钟有60s、1小时有60分钟、一天有24小时…但是要做到精准的计算或者格式化日期往往需要一个比较复杂的设计范式。 比较幸运的是,苹果的标准库中提供了完整的一套关于日期计算的API。虽然这些API猛的看起来比较复杂,但是能帮我们统一处理由于各地区文化不同带来的日期表现形式的不同等问题。 在这篇文章中,看下在日常的开发中怎么更好的使用这些API,从而更加高效精准的处理日期相关的问题。
TimeInterval和Calendar的使用区别
TimeInterval是一个Double类型的类型别名,可以非常方便的表示当前时间点后多少秒的一个时间点。比如要在20s后发送一个通知:
let date = Date().addingTimeInterval(20)
schedule(notification, for: date)
因为TimeInterval的精度到秒,所以当用TimeInterval来表示一个较长的时间段时,有时候会带来误差,比如:
let tomorrow = Date().addingTimeInterval(60 * 60 * 24)
这里的tomorrow表示明天的现在,但并不一定是精确。
这时就要使用Calendar了,Calendar可以精确到毫秒级(此外Calendar是匹配当前用户所在时区的)!让我们看下怎么使用Calendar来精确的实现tomorrow。
let calendar = Calendar.current
let date = Date()
let components = calendar.dateComponents(
[.hour, .minute, .second, .nanosecond],
from: date
)
let tomorrow = calendar.nextDate(
after: date,
matching: components,
matchingPolicy: .nextTime
)
扩展Date
Calendar可以帮我们精确的获取到tomorrow-now这个时间点。但每次我们想要获取这个时间点时都要写这一串代码。这时候我们通常要将这段代码封装到一个方法中(通过扩展方法我们可以更加灵活的实现个性的需求),然后通过一个静态变量加入到对应类型的扩展中:
extension Date {
func sameTimeNextDay(
inDirection direction: Calendar.SearchDirection = .forward,
using calendar: Calendar = .current
) -> Date {
let components = calendar.dateComponents(
[.hour, .minute, .second, .nanosecond],
from: self
)
return calendar.nextDate(
after: self,
matching: components,
matchingPolicy: .nextTime,
direction: direction
)!
}
}
extension Date {
static var currentTimeTomorrow: Date {
return Date().sameTimeNextDay()
}
static var currentTimeYesterday: Date {
return Date().sameTimeNextDay(inDirection: .backward)
}
}
通过Calender获取某时间段的开始或结束点
Calender提供了一套api可以方便的获取某个时间段的开始和结束点。比如一天的开始及结束点、一周的开始及结束点…
比如:我们的某个接口数据只有在新的一天的开始的时候才会更新,那么我们在同一天内就没必要每次都从后台获取数据了。只需要从本地缓存取就可以了,直到第二天的来临。
func contentDidLoad(_ content: Content) {
let refreshDate = calendar.startOfDay(for: .currentTimeTomorrow)
cache(content, until: refreshDate)
}
通过Calender获取某时间点对应的某一段时间
Calender提供了一套api可以方便的获取某个时间点对应的某一段时间。 比如获取当前天、月、年。
let date = Date()
let today = calendar.dateInterval(of: .day, for: date)
let currentMonth = calendar.dateInterval(of: .month, for: date)
let currentYear = calendar.dateInterval(of: .year, for: date)
还可以获取某个时间点后的某一段时间。 比如获取明天开始对应的一周:
let nextWeekend = calendar.nextWeekend(startingAfter: Date())!
showPartySchedulingView(
withStartDate: nextWeekend.start,
endDate: nextWeekend.end
)