英文:
Check if date is 1 second away from new date
问题
I want to check if the current date input is 1 second away from new date (Date format: "2023-05-20T23:59:59.000+05:30"). Below is my Date extension.
extension Date {
var isOneSecondAway: Bool {
let calendar = Calendar.current
let nextStartOfDay = calendar.date(byAdding: .day, value: 1, to: self.startOfDay) ?? self
let timeIntervalInSeconds = Int(nextStartOfDay.startOfDay.timeIntervalSince(self))
return timeIntervalInSeconds == 1
}
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
let targetDateString = "2023-05-20T23:59:59.000+05:30"
guard let targetDate = dateFormatter.date(from: targetDateString) else {return}
print(targetDate.isOneSecondAway)
Expected output for "2023-05-20T23:59:59.000+05:30" is true but it's returning false. Am I doing something wrong here?
<details>
<summary>英文:</summary>
I want to check if the current date input is 1 second away from new date (Date format: "2023-05-20T23:59:59.000+05:30"). Below is my Date extension.
extension Date {
var isOneSecondAway: Bool {
let calendar = Calendar.current
let nextStartOfDay = calendar.date(byAdding: .day, value: 1, to: self.startOfDay) ?? self
let timeIntervalInSeconds = Int(nextStartOfDay.startOfDay.timeIntervalSince(self))
return timeIntervalInSeconds == 1
}
var startOfDay: Date {
return Calendar.current.startOfDay(for: self)
}
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
let targetDateString = "2023-05-20T23:59:59.000+05:30"
guard let targetDate = dateFormatter.date(from: targetDateString) else {return}
print(targetDate.isOneSecondAway)
Expected output for "2023-05-20T23:59:59.000+05:30" is true but its returning false. Am I doing something wrong here?
</details>
# 答案1
**得分**: 1
You need a timezone in order to figure out when the "start of day" is, and to a lesser extent, when the "next day" is.
You used `Calendar.current` in your `Date` extensions, so the timezone of `Calendar.current` will be used to do the date calculations. This is usually the system timezone. Note that a `Date` object does not have an associated timezone. `Calendar` does.
I suspect that the timezone of `Calendar.current` *isn't* at an offset of +05:30 for the date and time that you used.
If you set the timezone explicitly to be at an offset of +05:30, then the code prints `true`:
```swift
extension Date {
var isOneSecondAway: Bool {
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 5 * 60 * 60 + 30 * 60)!
let nextStartOfDay = calendar.date(byAdding: .day, value: 1, to: self.startOfDay) ?? self
let timeIntervalInSeconds = Int(nextStartOfDay.startOfDay.timeIntervalSince(self))
return timeIntervalInSeconds == 1
}
var startOfDay: Date {
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 5 * 60 * 60 + 30 * 60)!
return calendar.startOfDay(for: self)
}
}
Of course, this wouldn't be very useful if your date strings can contain timezones other than +05:30.
To handle all kinds of timezones, the Date
extensions need a timezone parameter, and you need to extract the timezone from the strings.
extension Date {
func isOneSecondAway(timeZone: TimeZone) -> Bool {
var calendar = Calendar.current
calendar.timeZone = timeZone
let nextStartOfDay = calendar.date(byAdding: .day, value: 1, to: self.startOfDay(in: timeZone)) ?? self
let timeIntervalInSeconds = Int(nextStartOfDay.startOfDay(in: timeZone).timeIntervalSince(self))
return timeIntervalInSeconds == 1
}
func startOfDay(in timeZone: TimeZone) -> Date {
var calendar = Calendar.current
calendar.timeZone = timeZone
return calendar.startOfDay(for: self)
}
}
// ...
if let targetDate = dateFormatter.date(from: targetDateString),
let timeZone = TimeZone(iso8601: targetDateString) {
print(targetDate.isOneSecondAway(timeZone: timeZone))
}
Here I used a TimeZone
initializer, adapted from this post.
extension TimeZone {
init?(iso8601: String) {
// assuming there are 23 characters before the offset,
// like your format string suggests
let tz = iso8601.dropFirst(23) // remove date and time part
if tz == "Z" {
self.init(secondsFromGMT: 0)
} else if tz.count == 3 { // assume +/-HH
if let hour = Int(tz) {
self.init(secondsFromGMT: hour * 3600)
return
}
} else if tz.count == 5 { // assume +/-HHMM
if let hour = Int(tz.dropLast(2)), let min = Int(tz.dropFirst(3)) {
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
} else if tz.count == 6 { // assume +/-HH:MM
let parts = tz.components(separatedBy: ":")
if parts.count == 2 {
if let hour = Int(parts[0]), let min = Int(parts[1]) {
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
}
}
return nil
}
}
英文:
You need a timezone in order to figure out when the "start of day" is, and to a lesser extent, when the "next day" is.
You used Calendar.current
in your Date
extensions, so the timezone of Calendar.current
will be used to do the date calculations. This is usually the system timezone. Note that a Date
object does not have an associated timezone. Calendar
does.
I suspect that the timezone of Calendar.current
isn't at an offset of +05:30 for the date and time that you used.
If you set the timezone explicitly to be at an offset of +05:30, then the code prints true
:
extension Date {
var isOneSecondAway: Bool {
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 5 * 60 * 60 + 30 * 60)!
let nextStartOfDay = calendar.date(byAdding: .day, value: 1, to: self.startOfDay) ?? self
let timeIntervalInSeconds = Int(nextStartOfDay.startOfDay.timeIntervalSince(self))
return timeIntervalInSeconds == 1
}
var startOfDay: Date {
var calendar = Calendar.current
calendar.timeZone = TimeZone(secondsFromGMT: 5 * 60 * 60 + 30 * 60)!
return calendar.startOfDay(for: self)
}
}
Of course, this wouldn't be very useful if your date strings can contain timezones other than +05:30.
To handle all kinds of timezones, the Date
extensions need a timezone parameter, and you need to extract the timezone from the strings.
extension Date {
func isOneSecondAway(timeZone: TimeZone) -> Bool {
var calendar = Calendar.current
calendar.timeZone = timeZone
let nextStartOfDay = calendar.date(byAdding: .day, value: 1, to: self.startOfDay(in: timeZone)) ?? self
let timeIntervalInSeconds = Int(nextStartOfDay.startOfDay(in: timeZone).timeIntervalSince(self))
return timeIntervalInSeconds == 1
}
func startOfDay(in timeZone: TimeZone) -> Date {
var calendar = Calendar.current
calendar.timeZone = timeZone
return calendar.startOfDay(for: self)
}
}
// ...
if let targetDate = dateFormatter.date(from: targetDateString),
let timeZone = TimeZone(iso8601: targetDateString) {
print(targetDate.isOneSecondAway(timeZone: timeZone))
}
Here I used a TimeZone
initialiser, adapted from this post.
extension TimeZone {
init?(iso8601: String) {
// assuming there are 23 characters before the offset,
// like your format string suggests
let tz = iso8601.dropFirst(23) // remove date and time part
if tz == "Z" {
self.init(secondsFromGMT: 0)
} else if tz.count == 3 { // assume +/-HH
if let hour = Int(tz) {
self.init(secondsFromGMT: hour * 3600)
return
}
} else if tz.count == 5 { // assume +/-HHMM
if let hour = Int(tz.dropLast(2)), let min = Int(tz.dropFirst(3)) {
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
} else if tz.count == 6 { // assime +/-HH:MM
let parts = tz.components(separatedBy: ":")
if parts.count == 2 {
if let hour = Int(parts[0]), let min = Int(parts[1]) {
self.init(secondsFromGMT: (hour * 60 + min) * 60)
return
}
}
}
return nil
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论