DateFormatter.localizedString is broken for some o

2019-08-23 06:53发布

问题:

This is what I do when picker changes:

extension Date {
    var fromCurrentToUTC: Date {
        return addingTimeInterval(-TimeInterval(TimeZone.current.secondsFromGMT()))
    }
}    


var title = "--"

if let date = datePickerView.date?.fromCurrentToUTC {
    title = DateFormatter.localizedString(from: date, dateStyle: .medium, timeStyle: .none)
}

print("-----")
print(datePickerView.date!)
print(title)
print(TimeZone.current)
print(datePickerView.date!.timeIntervalSince1970)

dateTimeSegmentControl.setTitle(title, forSegmentAt: 0)

And this is how it looks for the dates:

Assuming. Everything is fine for the dates before 6th November, and everything is off after 6th November. Why?

update:

That critical date is different for every time zone I use. For example:

Warsaw (+0200) the date is 30 October Chicago (-0500) the date is 6th November

The ordered prints:

-----
2017-11-04 00:00:00 +0000
4 Nov 2017
America/New_York (current)
1509753600.0
-----
2017-11-05 00:00:00 +0000
5 Nov 2017
America/New_York (current)
1509840000.0
-----
2017-11-06 00:00:00 +0000
5 Nov 2017
America/New_York (current)
1509926400.0
-----
2017-11-07 00:00:00 +0000
6 Nov 2017
America/New_York (current)
1510012800.0

回答1:

In your function

extension Date {
    var fromCurrentToUTC: Date {
        return addingTimeInterval(-TimeInterval(TimeZone.current.secondsFromGMT()))
    }
} 

the GMT offset of the current date is subtracted, not by the GMT offset of the date to be adjusted. Therefore you get a wrong result if the date to be adjusted is in a DST period and the current date is not, or vice versa.

That can be fixed by using

extension Date {
    var fromCurrentToUTC: Date {
        return addingTimeInterval(-TimeInterval(TimeZone.current.secondsFromGMT(for: self)))
    }
} 

instead. However, a better solution would be to set the timezone of the date formatter to UTC, instead of adjusting the date:

if let date = datePickerView.date {
    let fmt = DateFormatter()
    fmt.dateStyle = .medium
    fmt.timeStyle = .none
    fmt.timeZone = TimeZone(secondsFromGMT: 0)
    let title = fmt.string(from: date)
    print(title)
}