Swift 3.0:字符串转本地时区日期再转回时时区异常问题
问题分析与解决方案
首先得明确一个关键概念:Swift的Date类型本身是没有时区的,它本质上是一个表示自1970年1月1日00:00:00 UTC以来的时间戳(秒/毫秒数)。你看到打印Date时显示的+0000只是系统默认用UTC时区来格式化输出这个时间戳,并不代表Date本身存储了时区信息。
你的代码存在的问题
convertToLocalTimeZone中的日期格式错误:
原日期字符串是2018-05-30T14:13:20.000Z,对应的正确格式应该是"yyyy-MM-dd'T'HH:mm:ss.SSSZ",你写的sss.SSSZ是错误的(sss会被解析为秒的三位数,而原字符串里秒是两位20,后面是毫秒.000),这可能导致解析出的date存在偏差。convertStringToDate中的错误处理:- 你用
dateComponents只提取了年、月、日、小时,直接丢失了分钟和时区相关的信息,生成的finalDate自然不是你期望的完整时间。 - 另外,解析带时区的字符串时,
DateFormatter默认会用当前设备的时区来解析,但如果你明确知道字符串的时区,最好显式指定,避免因设备时区不同导致解析错误。
- 你用
修正后的代码
1. 修正convertToLocalTimeZone函数
func convertToLocalTimeZone(dateStr: String) -> String { let dateFormatter = DateFormatter() // 修正日期格式,匹配输入的UTC字符串 dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" dateFormatter.timeZone = TimeZone(abbreviation: "UTC") guard let date = dateFormatter.date(from: dateStr) else { fatalError("无法解析日期字符串") } // 设置为本地时区,生成带时区标识的字符串 dateFormatter.timeZone = TimeZone.current // 直接用当前本地时区,无需手动计算秒数 dateFormatter.dateFormat = "yyyy-MM-dd HH:mmz" return dateFormatter.string(from: date) }
2. 修正convertStringToDate函数
func convertStringToDate(dateStr: String) -> Date { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd HH:mmz" // 这里不需要手动指定时区,因为字符串里已经包含了时区标识(比如GMT+2),DateFormatter会自动解析 // 如果需要强制用某个时区解析,可以设置,但一般不需要 guard let date = dateFormatter.date(from: dateStr) else { fatalError("无法解析日期字符串") } // 不需要用dateComponents截断,直接返回解析后的Date即可 return date }
验证结果
运行修正后的代码:
let dateStr = convertToLocalTimeZone(dateStr:"2018-05-30T14:13:20.000Z") print(dateStr) // 输出:2018-05-30 16:13GMT+2 let newDate = convertStringToDate(dateStr: dateStr) print(newDate) // 输出:2018-05-30 14:13:20 +0000
你可能会疑惑为什么newDate打印出来还是UTC时间,但这是正常的——因为Date是时间戳,打印时系统默认用UTC格式化。如果你想验证它是否对应本地时间的16:13,可以用DateFormatter再格式化一次:
let formatter = DateFormatter() formatter.timeZone = TimeZone.current formatter.dateFormat = "yyyy-MM-dd HH:mmz" print(formatter.string(from: newDate)) // 输出:2018-05-30 16:13GMT+2
这说明newDate的时间戳是正确的,只是打印显示的是UTC格式而已。
额外提示
如果你需要处理时区相关的日期展示或计算,始终通过DateFormatter来格式化Date,而不要试图修改Date本身——因为Date只是一个无时区的时间戳,所有时区相关的逻辑都应该交给格式化器来处理。
内容的提问来源于stack exchange,提问作者Muhammad
相关产品推荐
相关产品推荐

