检查日期是否离新日期相差1秒。

huangapple go评论64阅读模式
英文:

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: &quot;2023-05-20T23:59:59.000+05:30&quot;). 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 = &quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss.SSSZZZZZ&quot;
    let targetDateString = &quot;2023-05-20T23:59:59.000+05:30&quot;
    guard let targetDate = dateFormatter.date(from: targetDateString) else {return}
    print(targetDate.isOneSecondAway)

Expected output for &quot;2023-05-20T23:59:59.000+05:30&quot; 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) -&gt; 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) -&gt; 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 == &quot;Z&quot; {
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: &quot;:&quot;)
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
}
}

huangapple
  • 本文由 发表于 2023年5月22日 17:36:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76304811.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定