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类型

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

常用属性

1、dateFormat

指定日期字符串的格式,用于解析(字符串 → 日期)或格式化(日期 → 字符串)。

formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

注意:这个属性和 dateStyle / timeStyle 是互斥的,不能同时设置。

2、locale

决定月份名称、星期名、AM/PM 格式的语言。

formatter.locale = Locale(identifier: "en_US_POSIX")

推荐使用en_US_POSIX,因为它是一种“标准化”语言环境,永远使用英文格式,避免用户设备语言导致解析失败(特别是 CSV/JSON 里的标准格式)。

3、timeZone

决定日期字符串如何解析为 Date(内部时间)。

formatter.timeZone = TimeZone(secondsFromGMT: 0) // UTC时间

如果不设置时区,就会使用系统当前时区,可能导致时间误差。

4、calendar

决定日期的计算方式(公历、伊斯兰历、佛历等)。

formatter.calendar = Calendar(identifier: .gregorian)

通常使用公历(gregorian),但如果处理的是特定地区数据,就可能要换。

5、dateStyle / timeStyle

让系统自动决定日期和时间的格式(适合展示给用户)。

可选值

.short:24/3/25

.medium:Mar 24, 2025

.long:March 24, 2025

.full:Monday, March 24, 2025

示例

formatter.dateStyle = .medium
formatter.timeStyle = .short

注意:只适合展示用,不适合解析固定格式的数据。

6、isLenient

设置为 true 时,允许解析时对不规则格式宽容。

formatter.isLenient = true

示例

formatter.dateFormat = "yyyy-MM-dd"
formatter.date(from: "2025-2-5") // 正常解析,虽然格式不对齐

7、doesRelativeDateFormatting

启用后,日期会显示为 “今天”、“明天”、“昨天”等。

formatter.doesRelativeDateFormatting = true

只适用于 dateStyle / timeStyle 模式,且仅在部分语言中生效。

SwiftUI显示日期

可以使用 DateFormatter 来设置日期的格式,在SwiftUI中使用 Text 来格式化和显示日期。

代码示例

import SwiftUI

struct ContentView: View {
    // 当前日期
    let currentDate = Date()

    var body: some View {
        VStack {
            // 显示当前日期
            Text(formattedDate(currentDate))
                .font(.largeTitle)
                .padding()
        }
    }

    // 格式化日期
    func formattedDate(_ date: Date) -> String {
        let formatter = DateFormatter()
        formatter.dateStyle = .medium // 设置日期样式
        formatter.timeStyle = .none // 设置不显示时间
        return formatter.string(from: date)
    }
}

@main
struct MyApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

例如下图的历史时间:

据需要调整 DateFormatter 的 dateStyle 和 timeStyle 来改变日期和时间的显示格式。

如果想显示不同的日期格式或更复杂的格式化,可以参考以下格式:

formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"

处理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的内容。

   

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

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

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