从 Swift 5.5 和 iOS 15 开始,我们有了一个新的格式化程序 API,它允许我们以更具声明性和直观性的方式显示字符串日期。在深入研究之前,让我们回顾一下实际的格式化程序 API 如何与以下示例一起工作。
extension DateFormatter {
static let MMddyy: DateFormatter = {
let formatter = DateFormatter()
formatter.timeZone = TimeZone(abbreviation: "UTC") //TimeZone.current
formatter.dateFormat = "MM/dd/yy"
return formatter
}()
}
extension Date {
func formatToString(using formatter: DateFormatter) -> String {
return formatter.string(from: self)
}
}
let date = Date()
print(date.formatToString(using: .MMddyy) // 07/18/2021
这已经足够了。我们使用静态工厂模式来创建一个可重用的 DateFormatter,然后我们创建一个实用函数来将 Date 解析为 String,给定特定的格式化程序
新的格式化程序 API 解决了这个问题,让我们可以描述我们想要显示日期的方式,而不是配置 DateFormatter。我们现在有四种格式函数可供使用:
// 1.
func formatted() -> String
// 2.
func formatted<F>(_ format: F)
-> F.FormatOutput where F : FormatStyle, F.FormatInput == Date
// 3.
func ISO8601Format(_ style: Date.ISO8601FormatStyle = .init()) -> String
// 4.
func formatted(
date: Date.FormatStyle.DateStyle,
time: Date.FormatStyle.TimeStyle) -> String
让我们从最基本的例子开始,使用选项一。这会将我们的日期转换为默认的字符串格式(日期 + 时间)。
let date = Date.now
print(date.formatted()) // 07/01/2021, 1:38 PM
我们无法进行任何转换,但在某些情况下,这种格式对我们来说已经足够了。如果我们想自定义日期格式,我们需要使用第二个选项。
func formatted<F>(_ format: F)
-> F.FormatOutput where F : FormatStyle, F.FormatInput == Date
一开始有点棘手,但是一旦您了解如何实际使用它就会变得更容易。
我们需要提供一个 FormatStyle 作为参数才能使用该函数。
此 FormatStyle 是一种协议,它具有两种关联类型:输入和输出。在这种情况下,输入需要是 Date 类型。幸运的是,Swift 已经为我们提供了一个符合我们需要的 FormatStyle 协议的结构体:Date.FormatStyle。这个 FormatStyle 有一个静态变量dateTime: Date.FromatStyle,我们可以直接用作函数的参数。
我们现在有一个 FormatStyle 实例,我们可以使用Date.FormatStyle的实例方法对其进行自定义(您可以查看官方文档中的列表)。让我们看几个例子。
let date = Date.now
var stringDate =
date.formatted(
.dateTime
.month(.wide)
.day(.twoDigits)
.year()
)
print(stringDate) // July 01, 2021
date.formatted(
.dateTime
.month(.narrow)
.day()
.year(.twoDigits)
)
这里我们使用 dateTime 变量(它是一个 Date.FromatStyle 实例)并调用月、日和年函数。所有三个函数都返回一个 Date.FromatStyle 类型,这是函数参数所期望的类型。我们只是在操纵格式。我们可以使用这些实例方法实现很多组合。
请注意,我们调用函数的顺序不会影响最终输出。Swift 为我们做了繁重的工作,并根据用户的喜好决定正确的格式。
现在想象一下,我们需要以特定格式向后端发送一个字符串日期?(如“2021-07-18”)。对于这种情况,我们需要使用选项三。与 Date.FormatStyle 一样,ISO8601FormatStyle 有一个静态变量供我们使用,iso8601。这个格式函数与我们之前看到的函数非常相似,但有一些额外的配置可用。
let date = Date.now
let stringDate =
date.formatted(
.iso8601
.month(.twoDigits)
.day(.twoDigits)
.year()
.dateSeparator(.dash)
)
print(stringDate) // 2021-07-18
使用 ISO8601FormatStyle 我们可以指定我们想要使用的日期分隔符。
现在,如果我们只对显示日期字符串感兴趣,但我们对格式没有特定要求,我们可以使用最后一个选项(第四个)和一些预定义的格式。Date.FormatStyle.DateStyle 和 Date.FormatStyle.TimeStyle 都为我们提供了几个随时可用的静态常量
let date = Date.now
let stringDate =
date.fromatted(date: .long, time: .omitted)
print(stringDate) // July 18, 2021
后缺少的部分是相反的部分,我们如何从具有特定格式的字符串创建日期类型?为此,我们必须使用新的 Date 初始值设定项:
init<T, Value>(_ value: Value, strategy: T) throws
where T : ParseStrategy, Value : StringProtocol,
T.ParseInput == String, T.ParseOutput == Date
我们必须向函数传递一个字符串(它将是具有自定义格式的字符串类型中的日期)和一个解析该字符串的策略。此策略参数必须是ParseStrategy类型。与FormatStyle一样,ParseStrategy也是一个协议,并且还有两个相关的类型(输入和输出)。输入必须是字符串,输出必须是日期。
对我们来说好消息是,像FormatStyle.Date一样,我们已经有一个符合ParseStrategy协议的内置结构:Date.ParseStrategy。我们只需要创建一个新实例即可将其用作 Date 的 init 函数中的参数。
init(
format: Date.FormatString,
locale: Locale? = nil,
timeZone: TimeZone,
calendar: Calendar = Calendar(identifier: .gregorian),
isLenient: Bool = true,
twoDigitStartDate: Date = Date(timeIntervalSince1970: 0)
)
假设我们将从后端接收一个日期字符串,格式如下:dd-MM-yyyy(例如 31-01-2021)。让我们首先创建我们的 ParseStrategy 实例,然后使用解析创建一个新的 Date 实例。
let parseStrategy =
Date.ParseStrategy(
format: "\(day: .twoDigits)-\(month: .twoDigits)-\(year: .defaultDigits)",
locale: Locale(identifier: "es"),
timeZone: .current
)
let serverDate = try? Date("01-08-2021", strategy: parseStrategy)
由于格式是 Date.FromatString 类型,我们可以使用插值初始值设定项结合 Date.FormatStyle.Symbol 来创建我们的日期格式。
请注意,在我写这篇文章的时候,所有这些功能都处于测试阶段,正式版本可能会有变化。
加入我们一起学习SwiftUI
QQ:3365059189
SwiftUI技术交流QQ群:518696470
教程网站:www.openswiftui.com