Swift - 从 ISO8601 日期字符串中检索时区

Posted

技术标签:

【中文标题】Swift - 从 ISO8601 日期字符串中检索时区【英文标题】:Swift - Retrieve timezone from ISO8601 date string 【发布时间】:2018-05-17 06:12:52 【问题描述】:

我有日期以这种格式保存在数据库中 - “yyyy-MM-dd'T'HH:mm:ssZ”。例如,“2018-05-17T11:15:00+0330”。时区各不相同,无论用户的本地时区是什么。

我想检索并显示像“2018 年 5 月 17 日 11.15AM”这样的日期,将日期/时间与保存时保持在同一时区。我该怎么做?

我试过了。

let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
//formatter.timeZone = ???
print(formatter.date(from: dateString))

但是我得到了本地时区的 Date 对象,并将其转换为日期字符串也会显示我本地时区的日期。

在 Swift 中有这样的东西吗,https://***.com/a/46216251/1373592?

谢谢。

【问题讨论】:

【参考方案1】:

你真正想做的是忽略时区。

您可以从日期字符串中去掉最后 5 个字符,以一种格式对其进行解析,然后将其格式化为另一种格式:

let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
let date = formatter.date(from: String(dateString.dropLast(5)))! // note the stripping off here
formatter.dateFormat = "MMMM d, yyyy hh.mma"
print(formatter.string(from: date))

请注意,您无法将该日期与其他日期进行比较,因为您已经剥离了它的时区。

另外,SwiftDate 是一个类似于 JodaTime 的库。

【讨论】:

这给出了最终字符串的正确输出,但 date 位于错误的时区,如果需要与其他日期进行任何比较,您将得到意想不到的结果。 @rmaddy 是的,这是真的。添加了警告。【参考方案2】:

这是对TimeZone 的一个小扩展,我刚刚开始。您可以使用它从 ISO8601 日期字符串创建 TimeZone。它将以下列格式处理字符串中的时区:

Z(UTC 时间) +/-HH +/-HHmm +/-HH:mm

这是扩展名:

extension TimeZone 
    init?(iso8601: String) 
        let tz = iso8601.dropFirst(19) // remove yyyy-MM-ddTHH:mm:ss part
        if tz == "Z" 
            self.init(secondsFromGMT: 0)
         else if tz.count == 3  // assume +/-HH
            if let hour = Int(tz) 
                self.init(secondsFromGMT: hour * 3600)
                return
            
         else if tz.count == 5  // assume +/-HHMM
            if let hour = Int(tz.dropLast(2)), let min = Int(tz.dropFirst(3)) 
                self.init(secondsFromGMT: (hour * 60 + min) * 60)
                return
            
         else if tz.count == 6  // assime +/-HH:MM
            let parts = tz.components(separatedBy: ":")
            if parts.count == 2 
                if let hour = Int(parts[0]), let min = Int(parts[1]) 
                    self.init(secondsFromGMT: (hour * 60 + min) * 60)
                    return
                
            
        

        return nil
    

还有一个测试:

print(TimeZone(iso8601: "2018-05-17T11:15:00+0330"))

您可以结合此解析和格式化,以便最终结果在原始时区中。

let dateString = "2018-05-17T11:15:00+0330"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
formatter.locale = Locale(identifier: "en_US_POSIX")
if let date = formatter.date(from: dateString), let tz = TimeZone(iso8601: dateString) 
    let newFmt = DateFormatter()
    newFmt.dateStyle = .medium
    newFmt.timeStyle = .short
    newFmt.timeZone = tz
    let newString = newFmt.string(from: date)
    print(newString)

【讨论】:

我认为这处理负偏移不正确。例如。 “2020-01-01T09:15:00-02:15”从 GMT 产生 -6300 秒而不是 -8100 秒,因为此代码将小时部分视为负数,而将分钟部分视为正数。

以上是关于Swift - 从 ISO8601 日期字符串中检索时区的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Swift 中解析/创建格式为小数秒 UTC 时区(ISO 8601、RFC 3339)的日期时间戳?

Swift:将 ISO8601 转换为日期

在 ISO 8601 日期中,T 字符是强制性的吗?

Google 表格单元格中的 ISO-8601 字符串到日期

如何从busybox中的“日期”获取ISO8601秒格式?

Python - 将日期的字符串表示形式转换为 ISO 8601