欢迎使用 Time,这是一个简化日历、日期和时间处理的 Swift 包。
Time 由 Ctrl Group 构建,旨在简化日期和时间的操作,并避免使用标准 Date
对象时常犯的错误。
Foundation 库提供了一个 Date
对象,它表示一个独立于任何日历或时区的单一时间点。 这个对象需要与 DateFormatter
、 TimeZone
、 Locale
、 DateComponents
和 Calendar
结合使用,才能执行日历算术并创建有意义的日期和时间表示。
Time 使用这些工具并提供一个简单的 API,允许您表示 CalendarDate
、TimeOfDay
、Timestamp
、CalendarDateRange
、TimeRange
等。
TimeZone
)分组的类型。Time 使用 Swift Package Manager 分发。 要将其安装到项目中,请将其作为依赖项添加到您的 Package.swift 清单中。
let package = Package(
...
dependencies: [
.package(url: "https://github.com/ctrlgroup/Time.git", from: "1.0.0")
],
...
)
然后在您想使用它的地方导入 Time
import Time
CalendarDate
允许您在公历中表示日期,不包含时间,并且与时区无关。
您可以显式地使用日、月和年创建它们,也可以使用 Date
和 TimeZone
创建它们。
let europeLondon: TimeZone = ...
let date: Date = ...
// April 4th, 1994
let dateOfBirth = try? CalendarDate(day: 03, month: 04, year: 1994)
// Can also be created from a `Date`
let calendarDate = CalendarDate(date: date, timeZone: europeLondon)
实例化后,您可以查询它们的日、月、年,甚至星期几。
print(calendarDate.month)
print(calendarDate.dayOfWeek)
它们也可以转换回 Date
(表示该日午夜的时刻)
// Can be converted back to a `Date` using a time zone
let date: Date = calendarDate.date(in: europeLondon)
您还可以轻松地进行日历算术运算,通常无需进行任何复杂的 Calendar
转换。
let nextWeek = dateOfBirth + 7 // Adds 7 days
let difference: Int = nextWeek - dateOfBirth // 7
// Calendar dates are comparable
let firstOfMay = try? CalendarDate(day: 01, month: 05, year: 2022)
let tenthOfJune = try? CalendarDate(day: 10, month: 06, year: 2022)
tenthOfJune > firstOfMay // true
// Easily find weekdays (uses a Calendar to compute the weekday)
dateOfBirth.previous(weekday: .sunday) // Finds the previous Sunday
dateOfBirth.next(weekday: .sunday) // Finds the next Sunday
使用日历日期进行算术运算是一个简单的操作,因为底层原始值只是一个 Int
。 这意味着您无需担心使用 Calendar
的开销,并且可以轻松地执行诸如迭代 CalendarDateRange
 之类的操作。
我们添加了一些便捷方法,以便以易于阅读的方式编写 TimeInterval
。
let seconds: TimeInterval = 12.hours + 34.minutes + 56.seconds
let fiveDays: TimeInterval = 5.days
要仅表示时间(不包含日期),您可以使用 TimeOfDay
。 这对于记录重复提醒或闹钟的时间等非常有用。
可以通过各种不同的方式创建 TimeOfDay
let example1 = try? TimeOfDay(hour: 18, minute: 30, second: 0)
let example2 = try? TimeOfDay(secondsSinceMidnight: 3600 * 12)
let example3 = TimeOfDay(date: Date(), timeZone: europeLondon)
let example4 = 18⁝30 // Use the "tricolon" operator
let example5 = 12⁝34⁝56 // Also supports seconds
您可以对 TimeOfDay
和 TimeInterval
进行算术运算
let midday = 11⁝30 + 30.minutes
let elevenAM = 11⁝30 - 30.minutes
let fiveMins: TimeInterval = 06⁝00 - 05⁝55
let oneAM = 23⁝30 + (1.hours + 30.minutes) // Can span over midnight
它还支持各种字符串转换
let timeOfDay = 15⁝34⁝56
let locale = Locale(identifier: "en-US")
timeOfDay.string(style: (.short, .twelveHour), locale: locale) // 3:34 PM
timeOfDay.string(style: (.medium, .twentyFourHour)) // 15:34:56
// Converting from a string
let result = TimeOfDay(string: "3:34 PM",
style: (.short, .twelveHour),
locale: locale)
TimeOfDay
也是 Equatable
、Hashable
、Codable
和 Comparable
。
CalendarDateRange
允许您轻松表示可以迭代的日历日期范围,并使用 Range
的所有常用运算符。
// A calendar date range is simply a Swift Range
public typealias CalendarDateRange = Range<CalendarDate>
// Can be created in the usual way
let range = startDate ..< endDate
// It can be indexed using integers
sut[0] == startDate
sut[1] == startDate + 1
// Convenience initializers make calendar date ranges easy to understand
CalendarDateRange(firstDate: startDate, lastIncludedDate: lastDate)
CalendarDateRange(startDate: startDate, endDate: endDate)
TimeRange
稍微复杂一些,因为 TimeOfDay
不是严格连续的(当持续添加秒时,它们会循环)。 但是,能够在其上形成范围仍然很有用。
// Can be created in the usual way
let timeRange: TimeRange = 11⁝30 ..< 18⁝00
let timeRangeOverMidnight: TimeRange = 23⁝30 ..< 01⁝00
// Can test is a time exists in the range
timeRange.contains(12⁝30) // true
当您想要在特定日历日期上表示特定时间(但在任何时区)时,可以使用 DateTime
。
let calendarDate: CalendarDate = ...
let timeOfDay: timeOfDay = ...
let dateTime = DateTime(calendarDate: calendarDate, timeOfDay: timeOfDay)
// Can be created from a `Date` and `TimeZone`
let dateTime = DateTime(date: Date(), timeZone: europeLondon)
// Can be converted to a `Date` or `Timestamp`
let date = dateTime.date(in: europeLondon)
let timestamp = dateTime.timestamp(in: europeLondon)
大多数情况下,Timestamp
将是替换 Date
的更合适的类型。 它只是 Date
的封装,包含记录时的 TimeZone
,因此类似地,它表示一个单一的时间点,但包含理解它表示的日期和时间所需的上下文。
let timestamp = Timestamp(date: Date(), timeZone: europeLondon)
timestamp.day // 1st March
timestamp.timeOfDay // 12pm
timestamp.dateTime = // 1st March at 12pm
Timestamp
和 Date
之间的另一个区别在于它实现 Equatable
的方式。 由于 Date
基于 Double
,因此它会受到浮点算术运算不准确的影响。 这意味着比较两个 Date
对象有时会给出意想不到的结果…
let date1 = dateFormatter.date(from: "2022-01-01T12:34:56.1234Z")!
let date2 = Date(timeIntervalSinceReferenceDate: 662733296.123)
print(date1) // 2022-01-01 12:34:56 +0000
print(date2) // 2022-01-01 12:34:56 +0000
date1 == date2 // false
Timestamp
通过考虑彼此在一毫秒之内的日期是否相等来解决此问题。
Clock
是一个可以读取以了解当前日期和时间的类。 应该使用它来代替使用 Date()
初始化程序。 通过将 Clock
实例注入到您的代码中,您可以在测试中将其桩化,以便对依赖于当前时间的代码进行单元测试。
许多类型(CalendarDate
、TimeOfDay
等)也是 Equatable
、Comparable
、Hashable
和 Codable
。
使用 Codable
进行序列化时,该包将首选将类型保存为标准 ISO8601 字符串,例如
2022-01-01T12:30:00Z
代表 DateTime
2022-01-01
代表 CalendarDate
12:30:00
代表 TimeOfDay
Timestamp
需要 TimeZone
的上下文,因此将在 JSON 中进行如下编码
{
"timestamp": "...",
"timeZone": "Europe/London"
}
在此,timestamp
属性是一个编码的 Date
。 这将以默认方式(作为双精度值,自参考日期以来的秒数)进行编码,除非您指定使用 date encoding strategy 属性使用 ISO8601 表示形式
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
let date = try? encoder.encode(timestamp)
Time 试图提供一种更简单的方法来从日期和时间创建字符串,而无需直接使用 DateFormatter
类。
DateFormatter
类来提高性能。let dateFormat = "dd-MM-yyyy"
// Using Foundation
let date: Date = ...
let timeZone: TimeZone = ...
let dateFormatter = DateFormatter()
dateFormatter.timeZone = timeZone
dateFormatter.locale = .current
dateFormatter.setLocalizedDateFormatFromTemplate(dateFormat)
let result = dateFormatter.string(from: date)
// Using Time
let calendarDate: CalendarDate = ...
let result = calendarDate.string(withFormat: dateFormat, locale: .current)
TimeOfDay
也存在类似的方法,并且 CalendarDate
、TimeOfDay
、DateTime
和 Timestamp
上存在用于创建 ISO8601 字符串的附加方法。
Time 由 Ctrl Group 的团队设计和实现。
Time 在 MIT 许可下可用。 有关更多信息,请参见 LICENSE 文件。
我们希望 Time
在未来完全公开开发,您的贡献非常受欢迎!
如果您发现任何问题、有任何建议或希望改进文档,我们鼓励您打开一个 Pull Request,我们将积极审查并接受贡献。
我们希望您会发现此软件包有用且愉快!