Swift UI深入理解DateFormatter
Swift UI深入理解DateFormatter

Swift UI深入理解DateFormatter

文章介绍

本篇文章的主体是学习并理解DateFormatter,知道Date和String如何进行转换。在此,我们也可以灵活运用DateFormatter处理JSON文件。

DateFormatter是Swift中的一个类,用来将日期(Date类型)和字符串(String类型)进行相互转换。它可以根据特定的格式解析日期字符串,或将Date对象转换为特定格式的字符串。

例如,我们将一个日期字符串”2024-10-03”解析为Swift的Date类型。

首先,我们创建一个DateFormatter实例。

let formatter = DateFormatter()

设置DateFormatter的日期格式:

formatter.dateFormat = "y-MM-dd"

DateFormatter的.dateFormat属性用于指定日期和时间的显示格式。它接收一个格式字符串,用来定义日期和时间的输出样式。其中“y-MM-dd”是一种日期格式,表示年份、月份和日期。

  1. Y表示年份(Year),例如2024
  2. MM表示月份(Month),是两位数,零填充。例如01表示一月,12表示十二月。
  3. dd表示日期,也是两位数,零填充。例如01表示一号,31表示三十一号。
  4. HH表示小时,也是两位数,零填充。例如01表示1点,23表示23点。
  5. mm表示分钟,也是两位数,零填充。例如01表示1分,59表示59分。
  6. ss表示秒钟,也是两位数,零填充。例如13表示13秒。

这里可能存在疑问,那就是y和yyyy有什么区别?

我在这里整理了一个对应表格,不难看出,只有年份有部分规律,月、日、小时、分钟和秒钟,数字长度小于格式长度位数时,前面填0。

年份方面的规律:

  1. y格式在显示两位数年份时,会自动转换为四位数的年份,其余的位数都正常显示。
  2. yy格式是截取年份的后面两位数。
  3. 其他y格式,显示小于y位数时,年份前面添0,如yyy显示12时为012,但显示1234时,就会显示全部的年份。

常见的日期格式有:

  1. “yyyy-MM-dd”: 表示 2024-10-03(标准年-月-日格式)。
  2. “dd/MM/yyyy”: 表示 03/10/2024(日/月/年格式)。
  3. “MMM d, yyyy”: 表示 Oct 3, 2024(缩写月份、日期和年份)。
  4. “EEEE, MMMM d, yyyy”: 表示 Thursday, October 3, 2024(完整的星期几、月份、日期和年份)。

注意,我们的formatter是DateFormatter类型,不是Date类型,它是专门用于处理日期和字符串之间转换的类。

示例:格式化日期

let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"

let date = Date()  // 当前日期
let dateString = formatter.string(from: date)  // 将 Date 转为 "2024-10-06"
print(dateString)

示例:解析日期字符串

let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"

let dateString = "2024-10-06"
if let date = formatter.date(from: dateString) {
    print(date)  // 解析字符串为 Date 对象
}

根据上面两个示例,我们可以看到formatter有两种格式化方法,一种是将Date类型格式化为字符串,另一种是将字符串解析为Date类型。

formatter.string(from: Date())  // Date类型转换为String字符串
formatter.date(from: String)    // String字符串转换为Date类型

这两种方法可以让我们快速转换时间的两种格式,以便处理更多的使用场景。

处理JSON文件

JSON文件中,日期通常以字符串的形式存储,例如“1968-12-21”。当我们使用JSONDecoder解码JSON文件时,默认情况下,它不会自动将字符串转换为Date对象。所以,我们应该告诉JSONDecoder解码器如何识别并解析日期。

例如,我们有一个missions文件,其中有一个launchDate日期字符串字段,虽然launchDate是一个日期,但它实际是字符串类型。

"launchDate": "1968-10-11"

因此,我们可以通过DateFormatter定义解码的日期格式

let formatter = DateFormatter()
formatter.dateFormat = "y-MM-dd"  // 解析的日期格式

然后我们将DateFormatter定义的解码日期格式,给JSONDecoder的dateDecodingStrategy(日期解码策略)属性。

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)  // 告诉解码器使用这个格式来解析

dateDecodingStrategy是JSONDecoder的一个属性,用于定义从JSON字符串中解析日期,可以通过自定义日期格式解析JSON中的日期。

常见的dateDecodingStrategy有以下几种:

  1. .deferredToDate:使用 Date 自身的编码/解码方法。
  2. .secondsSince1970:将日期表示为自 1970 年 1 月 1 日 00:00:00 UTC 以来的秒数。
  3. .millisecondsSince1970:将日期表示为自 1970 年 1 月 1 日 00:00:00 UTC 以来的毫秒数。
  4. .iso8601:使用 ISO 8601 标准格式来解析日期(比如 “2024-10-03T00:00:00Z”)。
  5. .formatted(formatter):使用自定义的 DateFormatter 来解析日期。

所以,.formatted(formatter)就表示我们将自定义的DateFormatter传递给dateDecodingStrategy属性。然后,我们的JSONDecoder就可以按照我们设置的日期格式对JSON文件的日期字段进行解析。

此外,我们还需要知道的话,dateDecodingStrategy是一个属性,所以我们采用赋值的方式:

decoder.dateDecodingStrategy = .formatted(formatter)  // 告诉解码器使用这个格式来解析

将我们自定义的DateFormatter对象定义日期解析的格式,赋值给JSONDecoder的dateDecodingStrategy属性。

Date.formatted()方法

我们知道.formatted()是dateDecodingStrategy的一种策略,我们还可以扩展学习一下Date.formatted()方法。

这是Swift中Date类型的一个方法,用于将Date类型转换为字符串,所以跟前面提到的decoder.dateDecodingStrategy中的解析策略不同。Formatted()是更简洁的API,可以在Date()上直接调用.formatted()将Date转换为某种默认格式的字符串。

比如格式化当前日期:

let date = Date()
let formattedDate = date.formatted()  // 将当前日期转换为默认格式的字符串
print(formattedDate)  // 比如输出: "Oct 6, 2024"

我们在使用.formatted(formatter)表示通过一个自定义的格式化器(formatter)来对数据进行格式化。与前面的formatter.dateFormat相比更加简洁,但缺点就是不能更加灵活的自定义日期格式。

我们也可以传递自定义的格式选项来生成不同格式的字符串,比如:

let formattedDate = date.formatted(date: .abbreviated, time: .shortened)

这标识,我们将输出

Oct 6, 2024, at 22:04

Date().formatted(date:time)方法允许你根据不同的格式化选项将Date转换为字符串,对date和time参数,Swift提供了一些内置的选项,分别控制日期和时间部分的格式。

date参数选项:

  1. .omitted:不显示日期部分
  2. .numeric:日期以数字形式显示,例如“2024/10/6”
  3. .abbreviated:月份缩写,日期数字,年份缩写,例如” Oct 6, 24”
  4. .long:完整显示日期,例如“October 6, 2024”
  5. .complete:完整的日期格式,例如“Sunday, October 6, 2024”

time参数选项:

  1. .omitted:不显示时间部分
  2. .shortened:时间的简短表示,例如“22:09”
  3. .standard:标准时间显示,例如”22:10:00”
  4. .complete:完整的时间显示,例如“22:10:16 GMT+8”

因此,我们可以灵活允许这些选项来格式化日期和时间。

Date转换为字符串

当我们想要使用Text()展示Date数据时,代码如下:

Text(mission.launchDate)

通过Text()展示Date类型的数据时,会提示:

Cannot convert value of type 'String' to expected argument type 'Date'
Initializer 'init(_:)' requires that 'Date' conform to 'StringPro

1)我们可以根据上面提到的Date.formatted()方法进行转换:

Text(mission.launchDate?.formatted() ?? "N/A")

2)也可以使用formatter.string(from: Date())进行转换。

struct Mission: Codable, Identifiable {
    let launchDate: Date?
    ...
    var formattedLaunchDate: String {
    let formatter = DateFormatter()
    	formatter.dateStyle = .short
    	formatter.timeStyle = .short
    	return launchDate != nil ? formatter.string(from: launchDate!) : "N/A"
    }
}

Text(mission.formattedLaunchDate)

我们可以看到,我在mission结构体中定义了一个formattedLaunchDate计算属性,然后创建DateFormatter实例后,配置dateStyle和timeStyle,然后判断launchDate是否为nil,不为nil就强制解包,为nil的话就输出“N/A”。

这里之所以强制解包,是因为我们的launchDate是一个Date?可选项,所以,我们在使用formatter.string()时,就必须考虑launchDate为nil时,提供一个默认值。

因为formatter.string(from:)只接受Date类型,而我们希望在launchDate为nil时,输出字符串”N/A”,而不是一个Date内容。

因此,我们只能在开头先判断launchDate是否为nil,不是的话就强制解包,是的话就返回字符串“N/A”。

其他设想

有人可能会认为这个比较复杂,想要尝试在Text()中使用formatter.string(from:Date())输出字符串。

Text(mission.launchDate != nil ? DateFormatter().string(from: mission.launchDate!) : "N/A") 

这种方式只会导致为nil时输出”N/A”,而不会在不为nil时输出对应的日期和时间。

正如下图所示,只有第一个输出为“N/A”,其他的日期和时间都没有输出。

原因是我们的DateFormatter没有设置格式,默认格式可能会不符合预期,导致显示的内容不是想要的格式,甚至为空。因此,我们每次使用DateFormatter时必须正确格式化日期和时间的格式,才能正常输出。

另一种思路

let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none

我们将这段代码放在View中,然后通过return返回视图。

var body: some View {

    let formatter = DateFormatter()
    formatter.dateStyle = .long
    formatter.timeStyle = .none

    return NavigationStack {
        ... // 更多代码
        Text(mission.launchDate != nil ? formatter.string(from: mission.launchDate!) : "N/A")
        ... // 更多代码
    }
}

首先,解释一下为什么我们要在View视图当中使用return。这是因为当我们的body属性的类型是some View,因此我们需要返回一个符合View协议的对象。既然要返回,那就要用我们的return关键词。

formatter.dateStyle和formatter.timeStyle是DateFormatter的属性,用于定义日期和时间的样式。它们提供了一些预设的样式选项,适用于大多数常见的日期/时间格式。

  1. dateStyle:控制日期部分的格式(如年、月、如)。
  2. timeStyle:控制时间部分的格式(如时、分、秒)

日期和时间的预设值包括:

  1. .short: 短格式,例如 10/6/24 或 2:34 PM
  2. .medium: 中等长度,例如 Oct 6, 2024 或 2:34:00 PM
  3. .long: 长格式,例如 October 6, 2024
  4. .full: 完整格式,例如 Sunday, October 6, 2024
  5. .none: 表示不显示日期或时间。
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none

所以,我们的设置显示日期格式为October 6, 2024 ,并省略时间部分。

总结

我们学习到了DateFormatter是用于Date和String类型之间相互转换的类。在创建DateFormatter实例时,必须格式化日期或时间格式,否则可能会存在不显示的情况。

然后,我们学习了DateFormatter的日期和时间格式,以及格式化日期或解析日期字符串。

接着,我们学习如何处理JSON文件中的日期字段。并额外学习了解了Date.formatted()方法,它可以将Date类型转换为字符串,并且也可以设置日期和时间的格式。

最后,是在实际运用中将Date类型转换为字符串,可以使用Date.formatter()方法以及DateFormatter.string(Date())这两种方法处理,同时提到了在Text()中转换并输出的Date内容的设想,在另一种思路中,我们学习了dateStyle和timeStyle这两种日期或时间格式。

以上是全部的DateFormatter的内容。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注