ios 快速在时区之间转换日期

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/38641982/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 09:47:35  来源:igfitidea点击:

Converting date between timezones swift

iosswifttimezonensdategmt

提问by Alk

I have a date stored on my online server database which is in GMT. I load the date and convert it to the user's timezone using the following code :

我的在线服务器数据库中存储了一个日期,该数据库位于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
 }

Now I need to convert the date back to GMTin order to communicate it to the server, however I'm not sure how to convert it back to GMT.

现在我需要将日期转换回GMT以便将其传送到服务器,但是我不确定如何将其转换回GMT.

采纳答案by Amloelxer

Couldn't you just use your data formatter again with a different time zone and convert it? Such as

您不能再次使用具有不同时区的数据格式化程序并进行转换吗?如

dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let gmtDate = dateFormatter.dateFromString(string: "your old date as string here")

回答by mukaissi

Simpler version:

更简单的版本:

extension Date {
    func convertToTimeZone(initTimeZone: TimeZone, timeZone: TimeZone) -> Date {
         let delta = TimeInterval(timeZone.secondsFromGMT(for: self) - initTimeZone.secondsFromGMT(for: self))
         return addingTimeInterval(delta)
    }
}

回答by quemeful

about 50 times more effecient

效率提高约 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
    }
}

回答by Nikaaner

Based on mukaissi's answer, but the order of deductible in the expression has been corrected.

基于mukaissi 的回答,但已更正表达式中的免赔额顺序。

extension Date {    
    func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date {
        let delta = TimeInterval(initTimeZone.secondsFromGMT() - targetTimeZone.secondsFromGMT())
        return addingTimeInterval(delta)
    }    
}

回答by David Berry

Since NSDateis always in GMT/UTC the time zone only becomes relevant when displaying it to, or getting it from, the user. Just always assume it's UTC internally, convert it for the user (by setting it on the NSDateFormatter) as necessary, and you no longer have to worry about the problem.

由于NSDate始终采用 GMT/UTC,因此时区仅在向用户显示或从用户处获取时才变得相关。只要始终在内部假设它是 UTC,根据需要为用户转换(通过在 上设置NSDateFormatter),您就不必再担心这个问题了。

回答by mbi

So this is mukaissi's answerenhanced with valeCocoa's suggestionfor daylight saving time:

因此,这是mukaissi 的回答,根据 valeCocoa 的夏令时建议进行了增强:

func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date {
    let delta = TimeInterval(targetTimeZone.secondsFromGMT(for: self) - initTimeZone.secondsFromGMT(for: self))
    return addingTimeInterval(delta)
}

回答by dbplunkett

Amloelxer's answeris best if you want to convert a date string with no time offset to a Datein a particular time zone. However, for anybody looking to change a Datefrom one time zone to another while preserving the date and time from the starting time zone, please note that all the existing answers that use the GMT-offset delta will fail if the GMT offset differs between the initial Dateand the target Datein the target time zone, e.g. due to DST.

如果要将没有时间偏移的日期字符串转换Date为特定时区中的 a,Amloelxer 的答案是最好的。但是,对于希望Date在保留起始时区的日期和时间的同时将一个时区更改为另一个时区的任何人,请注意,如果 GMT 偏移量在初始时区之间不同,则所有使用 GMT 偏移量增量的现有答案都将失败Date以及Date目标时区中的目标,例如由于 DST。

At the time of writing, those answers also make other small mistakes in their implementations, but even using a "correct" version, converting 2020-03-08T05:00:00Zfrom GMT to EST will result in 2020-03-08T06:00:00-04:00, which is off by one hour. This bug doesn't occur if you adjust my example starting date by just a few hours forwards or backwards.

在撰写本文时,这些答案还在其实现中犯了其他小错误,但即使使用“正确”版本,2020-03-08T05:00:00Z从 GMT转换为 EST 也会导致2020-03-08T06:00:00-04:001 小时。如果您将我的示例开始日期向前或向后调整几个小时,则不会发生此错误。

The reason this occurs is because the GMT offset delta is calculated like this:

发生这种情况的原因是 GMT 偏移增量的计算方式如下:

initialTz.secondsFromGMT(for: initialDate) - targetTz.secondsFromGMT(for: initialDate)

secondsFromGMTtakes the date because a time zone's GMT offset differs based on the actual date in question (e.g. due to DST). So really the second operand should be targetTz.secondsFromGMT(for: targetDate), which is a catch-22. However, in most cases, targetTz.secondsFromGMT(for: targetDate)and targetTz.secondsFromGMT(for: initialDate)are equal. This is why only some dates produce the bug.

secondsFromGMT取日期是因为时区的 GMT 偏移量因所讨论的实际日期而异(例如,由于 DST)。所以实际上第二个操作数应该是targetTz.secondsFromGMT(for: targetDate),它是一个 catch-22。但是,在大多数情况下,targetTz.secondsFromGMT(for: targetDate)targetTz.secondsFromGMT(for: initialDate)是相等的。这就是为什么只有某些日期会产生错误的原因。

Calendrical calculations are complex, and attempting to roll your own will almost always result in buggy edge cases. There may also be other bugs in this case - this is just one I was able to come up with. Since a time zone is a calendrical unit, to avoid bugs, you should use the Calendar interface. For example, to convert GMT to EST:

日历计算很复杂,尝试自己进行计算几乎总是会导致错误的边缘情况。在这种情况下可能还有其他错误 - 这只是我能够想出的一个。由于时区是一个日历单位,为了避免错误,您应该使用日历界面。例如,要将 GMT 转换为 EST:

extension Calendar {

    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)
    }
}

// example values - replace these with the date and time zones for your use case
let initialDate = ISO8601DateFormatter().date(from: "2020-03-08T05:00:00+00:00")!
let initialTz = TimeZone(abbreviation: "GMT")!
let targetTz = TimeZone(abbreviation: "EST")!

var calendar = Calendar.current
calendar.timeZone = initialTz
let targetDate = calendar.dateBySetting(timeZone: targetTz, of: initialDate)!

回答by PierreMB

I suggest

我建议

  • you set the GMT timezone on your dateFormatterto get back directly a NSDate in UTC (having only NSDates in UTC is a good practice)
  • when you need to display it you use another NSDateFormatter with the local time zone set on it (it is by default)
  • when you need to send a date to your server, you use dateFormatteragain to generate a string
  • 您设置 GMT 时区dateFormatter以直接返回 UTC 中的 NSDate(在 UTC 中只有 NSDates 是一个好习惯)
  • 当您需要显示它时,您可以使用另一个 NSDateFormatter 并在其上设置本地时区(默认情况下)
  • 当您需要将日期发送到您的服务器时,您dateFormatter再次使用来生成一个字符串