下面的代码最初在matchingDate
字段中没有“ Z”,并显示如下结果:
true comparing 2017-08-28 13:06:54 +0000 to matching 2017-08-28 05:00:00 +0000
false comparing 2017-08-28 04:22:42 +0000 to matching 2017-08-28 05:00:00 +0000
false comparing 2017-08-28 00:00:01 +0000 to matching 2017-08-28 05:00:00 +0000
true comparing 2017-08-28 20:24:00 +0000 to matching 2017-08-28 05:00:00 +0000
所以我认为我的问题是UTC。但是,更正后(如下所示),它会打印:
false comparing 2017-08-28 13:06:54 +0000 to matching 2017-08-28 00:00:00 +0000
true comparing 2017-08-28 04:22:42 +0000 to matching 2017-08-28 00:00:00 +0000
true comparing 2017-08-28 00:00:01 +0000 to matching 2017-08-28 00:00:00 +0000
false comparing 2017-08-28 20:24:00 +0000 to matching 2017-08-28 00:00:00 +0000
这是意外的(所有4个都应匹配)。怎么了?
import Foundation
extension Array {
// src: https://stackoverflow.com/questions/54217704/cannot-use-mutating-member-because-append#comment-95266763
func appending<S: Sequence>(contentsOf newElements: S) -> Array where S.Element == Element {
return self + Array(newElements)
}
}
let dfCandidate = DateFormatter()
dfCandidate.dateFormat = "yyyy-MM-dd HH:mm:ssZ"
let dfMatching = DateFormatter()
dfMatching.dateFormat = "yyyy-MM-ddZ"
guard let matchingDate = dfMatching.date(from: "2017-08-28Z") else {
preconditionFailure()
}
let dates1 = [
"2017-08-28 13:06:54",
"2017-08-28 04:22:42"
]
let dates2 = [
"2017-08-28 00:00:01",
"2017-08-28 20:24:00"
]
let matchingDates: [Date] = dates1
.map { candidateDate in
guard let date = dfCandidate.date(from: candidateDate + "Z") else {
return nil
}
let isInDate = Calendar.current.isDate(date, equalTo: matchingDate, toGranularity: Calendar.Component.day)
print("\(isInDate) comparing \(String(describing: date)) to matching \(String(describing: matchingDate))")
return isInDate ? date : nil
}
.appending(contentsOf: dates2.map { candidateDate in
guard let date = dfCandidate.date(from: candidateDate + "Z") else {
return nil
}
let isInDate = Calendar.current.isDate(date, equalTo: matchingDate, toGranularity: Calendar.Component.day)
print("\(isInDate) comparing \(String(describing: date)) to matching \(String(describing: matchingDate))")
return isInDate ? date : nil
})
.compactMap { $0 }
print(matchingDates)
(注意:迅速4.2.1)
答案 0 :(得分:2)
问题在于您正在混合时区。
您在日期格式和您解析的日期字符串中使用Z
意味着这些日期字符串被视为UTC时区。那可能是您想要的,也可能不是。
您使用Calendar.current.isDate
意味着这两个日期是使用您自己的当前时区而非UTC时区进行比较的。因此,根据您的住所以及给定日期与午夜之间的距离,这两个日期可能在一个时区中是同一天,而在另一个时区中是两天。
您的代码可能完全正确,因为一旦您了解“奇怪”的结果实际上在给定的时区基础上是正确的。
您需要确定日期/时间字符串代表哪个时区。然后,您需要确定要与哪个时区进行比较。
示例(针对居住在美国东部,现在是UTC-5的人)
您解析字符串2017-08-28 13:06:54Z
。那是UTC时区的时间。您可以在2017-08-28 13:06:54 +0000
的输出中看到这一点。
您还解析了2017-08-28Z
。这被视为UTC午夜时间。打印此Date
将显示2017-08-28 00:00:00 +0000
。
在UTC时间中,这两个日期在同一天。
但是,当您使用Calendar.current
时,它会以您自己的本地时间(在本示例中为UTC-5)查看日期。
这意味着本地时间的第一个日期为2017-08-28 08:06:54 -0500
,本地时间的第二个日期为2017-08-27 19:00:00 -0500
。
在当地时间,这两个日期不在同一天。
解决方案:
如果您希望将所有日期都视为UTC日期,并且想要比较UTC时区中的每组日期,则应该将代码更新为以下内容:
let utc = TimeZone(secondsFromGMT: 0)!
let dfCandidate = DateFormatter()
dfCandidate.timeZone = utc
dfCandidate.dateFormat = "yyyy-MM-dd HH:mm:ss"
let dfMatching = DateFormatter()
dfMatching.timeZone = utc
dfMatching.dateFormat = "yyyy-MM-dd"
guard let matchingDate = dfMatching.date(from: "2017-08-28") else {
preconditionFailure()
}
let dates1 = [
"2017-08-28 13:06:54",
"2017-08-28 04:22:42"
]
let dates2 = [
"2017-08-28 00:00:01",
"2017-08-28 20:24:00"
]
var utcCalendar = Calendar.current
utcCalendar.timeZone = utc
let matchingDates: [Date] = dates1
.map { candidateDate in
guard let date = dfCandidate.date(from: candidateDate) else {
return nil
}
let isInDate = utcCalendar.isDate(date, equalTo: matchingDate, toGranularity: Calendar.Component.day)
print("\(isInDate) comparing \(String(describing: date)) to matching \(String(describing: matchingDate))")
return isInDate ? date : nil
}
.appending(contentsOf: dates2.map { candidateDate in
guard let date = dfCandidate.date(from: candidateDate + "Z") else {
return nil
}
let isInDate = utcCalendar.isDate(date, equalTo: matchingDate, toGranularity: Calendar.Component.day)
print("\(isInDate) comparing \(String(describing: date)) to matching \(String(describing: matchingDate))")
return isInDate ? date : nil
})
.compactMap { $0 }
print(matchingDates)
这将创建一个UTC时区并将其与两个日期格式符一起使用,并在UTC时区中创建一个日历集以进行日期比较。