我的在线服务器数据库中存有一个日期,该日期位于GMT
。我使用以下代码加载日期并将其转换为用户的时区:
if let messagedate = oneitem["timestamp"] as? String {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let date = dateFormatter.dateFromString(messagedate)
let source_timezone = NSTimeZone(abbreviation: "GMT")
let local_timezone = NSTimeZone.systemTimeZone()
let source_EDT_offset = source_timezone?.secondsFromGMTForDate(date!)
let destination_EDT_offset = local_timezone.secondsFromGMTForDate(date!)
let time_interval : NSTimeInterval = Double(destination_EDT_offset - source_EDT_offset!)
let final_date = NSDate(timeInterval: time_interval, sinceDate: date!)
curr_item.date = final_date
}
现在我需要将日期转换回GMT
以便将其传达给服务器,但我不知道如何将其转换回GMT
。
答案 0 :(得分:15)
难道您不能再使用不同时区的数据格式化程序并进行转换吗?如
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let gmtDate = dateFormatter.dateFromString(string: "your old date as string here")
答案 1 :(得分:11)
效率提高约50倍
extension Date {
func convertToLocalTime(fromTimeZone timeZoneAbbreviation: String) -> Date? {
if let timeZone = TimeZone(abbreviation: timeZoneAbbreviation) {
let targetOffset = TimeInterval(timeZone.secondsFromGMT(for: self))
let localOffeset = TimeInterval(TimeZone.autoupdatingCurrent.secondsFromGMT(for: self))
return self.addingTimeInterval(targetOffset - localOffeset)
}
return nil
}
}
答案 2 :(得分:6)
简单版本:
extension Date {
func convertToTimeZone(initTimeZone: TimeZone, timeZone: TimeZone) -> Date {
let delta = TimeInterval(timeZone.secondsFromGMT() - initTimeZone.secondsFromGMT())
return addingTimeInterval(delta)
}
}
答案 3 :(得分:4)
因此,mukaissi's answer的valeCocoa's suggestion增强了夏令时:
func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date {
let delta = TimeInterval(targetTimeZone.secondsFromGMT(for: self) - initTimeZone.secondsFromGMT(for: self))
return addingTimeInterval(delta)
}
答案 4 :(得分:3)
由于NSDate
始终采用GMT / UTC,因此时区仅在向用户显示或从中获取时才变得相关。只需始终在内部假设它的UTC,根据需要将其转换为用户(通过在NSDateFormatter
上设置),您不再需要担心这个问题。
答案 5 :(得分:2)
在撰写本文时,问题的代码很奇怪,答案对它的解释各不相同,而且有很多错误。如果您只想将没有时间偏移的日期 string 转换为特定时区的Date
,则Amloelxer's answer最好,但是对于为了解决其他人的问题,“如何在时区之间转换Date
”,有两种情况:
将Date
转换为另一个时区,同时保留初始时区中的日期和时间。
例如GMT到EST:2020-03-08T10:00:00Z
至2020-03-08T10:00:00-04:00
将Date
转换为另一个时区的日期和时间,同时保留初始时区。
例如格林威治标准时间(EST)到格林威治标准时间(GMT):2020-03-08T06:00:00-04:00
至2020-03-08T10:00:00-04:00
(因为初始Date
是格林尼治标准时间上午10点)
这两种情况实际上是相同的(示例开始和结束Date
是相同的),只是它们的措词不同以交换哪个时区是“初始”时区和哪个是“目标”。因此,如果您在两个时区之间交换时区,则以下两个解决方案是等效的,因此您可以选择一种在概念上更适合您的用例的解决方案。
extension Calendar {
// case 1
func dateBySetting(timeZone: TimeZone, of date: Date) -> Date? {
var components = dateComponents([.era, .year, .month, .day, .hour, .minute, .second, .nanosecond], from: date)
components.timeZone = timeZone
return self.date(from: components)
}
// case 2
func dateBySettingTimeFrom(timeZone: TimeZone, of date: Date) -> Date? {
var components = dateComponents(in: timeZone, from: date)
components.timeZone = self.timeZone
return self.date(from: components)
}
}
// example values
let initTz = TimeZone(abbreviation: "GMT")!
let targetTz = TimeZone(abbreviation: "EST")!
let initDate = Calendar.current.date(from: .init(timeZone: initTz, year: 2020, month: 3, day: 8, hour: 4))!
// usage
var calendar = Calendar.current
calendar.timeZone = initTz
let case1TargetDate = calendar.dateBySetting(timeZone: targetTz, of: initDate)!
let case2TargetDate = calendar.dateBySettingTimeFrom(timeZone: targetTz, of: initDate)!
// print results
let formatter = ISO8601DateFormatter()
formatter.timeZone = targetTz // case 1 is concerned with what the `Date` looks like in the target time zone
print(formatter.string(from: case1TargetDate)) // 2020-03-08T04:00:00-04:00
// for case 2, find the initial `Date`'s time in the target time zone
print(formatter.string(from: initDate)) // 2020-03-07T23:00:00-05:00 (the target date should have this same time)
formatter.timeZone = initTz // case 2 is concerned with what the `Date` looks like in the initial time zone
print(formatter.string(from: case2TargetDate)) // 2020-03-07T23:00:00Z
在撰写本文时,还有许多其他答案假定这两种情况之一,并且它们都共享一个错误-他们试图计算时区之间的时差,其中时差的符号确定情况:
情况1:
initialTz.secondsFromGMT(for: initialDate) - targetTz.secondsFromGMT(for: initialDate)
情况2:
targetTz.secondsFromGMT(for: initialDate) - initialTz.secondsFromGMT(for: initialDate)
secondsFromGMT
取您想知道其偏移量的Date
,因此在两种情况下,目标偏移量实际上应为targetTz.secondsFromGMT(for: targetDate)
,这是catch 22,因为我们不知道还不知道目标日期。但是,在Date
很近的大多数情况下,targetTz.secondsFromGMT(for: initialDate)
和targetTz.secondsFromGMT(for: targetDate)
相等-仅当它们不同时才会发生错误,当时间偏移在目标时区中的两个Date
之间发生变化时发生,例如用于DST。这是每种情况的一个错误示例:
extension Date {
// case 1 (bugged)
func converting(from initTz: TimeZone, to targetTz: TimeZone) -> Date {
return self + Double(initTz.secondsFromGMT(for: self) - targetTz.secondsFromGMT(for: self))
}
// case 2 (bugged)
func convertingTime(from initTz: TimeZone, to targetTz: TimeZone) -> Date {
return self + Double(targetTz.secondsFromGMT(for: self) - initTz.secondsFromGMT(for: self))
}
}
let formatter = ISO8601DateFormatter()
// case 1
do {
// example values
let initTz = TimeZone(abbreviation: "GMT")!
let targetTz = TimeZone(abbreviation: "EST")!
let initDate = Calendar.current.date(from: .init(timeZone: initTz, year: 2020, month: 3, day: 8, hour: 4))!
// usage
let targetDate = initDate.converting(from: initTz, to: targetTz)
// print results
formatter.timeZone = targetTz // case 1 is concerned with what the `Date` looks like in the target time zone
print(formatter.string(from: targetDate)) // 2020-03-08T05:00:00-04:00 (should be 4am)
}
// case 2
do {
// example values
let initTz = TimeZone(abbreviation: "EST")!
let targetTz = TimeZone(abbreviation: "GMT")!
let initDate = Calendar.current.date(from: .init(timeZone: initTz, year: 2020, month: 3, day: 8, hour: 1))!
// usage
let targetDate = initDate.convertingTime(from: initTz, to: targetTz)
// print results
formatter.timeZone = targetTz // for case 2, find the initial `Date`'s time in the target time zone
print(formatter.string(from: initDate)) // 2020-03-08T06:00:00Z (the target date should have this same time)
formatter.timeZone = initTz // case 2 is concerned with what the `Date` looks like in the initial time zone
print(formatter.string(from: targetDate)) // 2020-03-08T07:00:00-04:00 (should be 6am)
}
如果仅向前或向后几个小时调整示例日期,则不会发生该错误。日历计算很复杂,尝试自己滚动会导致边缘情况经常出现错误。由于时区是日历单位,因此为了避免错误,应使用现有的Calendar界面。
答案 6 :(得分:1)
基于mukaissi's answer,但表达式中的可抵扣顺序已得到纠正。
extension Date {
func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date {
let delta = TimeInterval(initTimeZone.secondsFromGMT() - targetTimeZone.secondsFromGMT())
return addingTimeInterval(delta)
}
}
答案 7 :(得分:0)
我建议
dateFormatter
上设置GMT时区以直接取回UTC中的NSDate(在UTC中仅使用NSDate是一种很好的做法)dateFormatter
生成字符串答案 8 :(得分:0)
基于mukaissi答案
import Foundation
extension Date {
func to(timeZone outputTimeZone: TimeZone, from inputTimeZone: TimeZone) -> Date {
let delta = TimeInterval(outputTimeZone.secondsFromGMT(for: self) - inputTimeZone.secondsFromGMT(for: self))
return addingTimeInterval(delta)
}
}
let utcTimeZone = TimeZone(abbreviation: "UTC")!
let dateString = "2020-06-03T01:43:44.888Z"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let date = dateFormatter.date(from: dateString)
print(date)
print(date?.to(timeZone: .autoupdatingCurrent, from: utcTimeZone))
print(date?.to(timeZone: .current, from: utcTimeZone))
print(date?.to(timeZone: TimeZone(abbreviation: "PDT")!, from: utcTimeZone))
不要忘记在此处粘贴解决方案1代码
extension DateFormatter {
func date(from string: String, timeZoneInString: TimeZone, outputTimeZone: TimeZone = .autoupdatingCurrent) -> Date? {
date(from: string)?.to(timeZone: outputTimeZone, from: timeZoneInString)
}
}
let utcTimeZone = TimeZone(abbreviation: "UTC")!
let pdtTimeZone = TimeZone(abbreviation: "PDT")!
let dateString = "2020-06-03T01:43:44.888Z"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
print(dateFormatter.date(from: dateString))
print(dateFormatter.date(from: dateString, timeZoneInString: utcTimeZone))
print(dateFormatter.date(from: dateString, timeZoneInString: utcTimeZone, outputTimeZone: pdtTimeZone))