Adding 1 hour to time method

I'm having an issue and could use some help on a time checking method I have written. The functionality should work as follows: Check the time stored in UserDefaults and if the time is more than 1 hour return true.

I've managed to get it working in some sort of way however when the time get's to 23:00 hour there is no 24. I could write an if else block to catch this but I wondered if there's a more simpler way?


Current functionality is:

private func checkTimer() -> Bool {
        let date = Date()
        let calender = Calendar.current
        var getTime = calender.dateComponents([.hour, .minute], from: date)
          let intTime = "\(getTime.hour!)\(getTime.minute!)"
        let calcTime = Int(intTime)! + 100 // This is where the time gets added to an int i.e. 1246
        print("TIME: Calculated time is \(calcTime)")
    
        let lastTimeAccessed = UserDefaults.standard.object(forKey: "Time") as? Int
    
        if (lastTimeAccessed != nil) {
        
            if lastTimeAccessed! >= calcTime {
                print("APP: Time has passed")
                UserDefaults.standard.set(intTime, forKey: "Time")
                return true
            } else {
                print("TIME: \(intTime)")
                print("APP: Not enough time has passed")
                return false
            }
        } else {
        
            UserDefaults.standard.set(intTime, forKey: "Time")
            return false
        }
    
    
    }



I could use some help on this.

Thanks

Accepted Reply

If you’re just using this to the limit the rate at which you generate requests, you don’t need to mess around with calendars at all. You can grab the current time and compare it to the previous time. For example:

self.then = Date()

… time passes …

let now = Date()
if now > (then + 3600) {
    self.then = now
    … do stuff …
}
Date
counts in seconds since a reference date in UTC, so it’s unaffected by time zone, locale, and so on. Also, while it’s a bad idea to hard-code values like the number of seconds in a day, in this case the number of seconds in an hour is always 3600 (modulo leap seconds, which you can reasonably ignore).

The only gotcha here relates to the user changing the clock. If they set the clock forward that’s not a problem (you may run an unnecessary request, but it doesn’t sound like that’s a big deal) but if they set the clock backwards your requests could be disabled for a long time. You can avoid this by:

  1. Registering for the

    NSSystemClockDidChangeNotification
    notification
  2. If that fires, setting your

    self.then
    value to
    Date.distantPast
    so the next request always runs

Date.distantPast

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Replies

Time is Hard™. Even the simplest-seeming tasks are hard to solve in the general case. In most cases the platform APIs provide the facilities to solve these problems; the hard part is actually defining the problem itself.

You wrote:

Check the time stored in UserDefaults and if the time is more than 1 hour return true.

You’ll need to be more specific about describing your requirements. For example:

  • How do you want to handle time zone changes?

  • What do you want to do with lost hours (during a daylight savings ‘spring forward’, for example)?

  • What do you want to do with duplicate hours (during a daylight savings ‘fall back’, for example)?

  • What do you want to happen if the device’s time changes (for example, when the user changes the clock)?

If you can fill in same of these blanks, we should be able to make reasonable suggestions for how to proceed.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hey, Time is Hard™ 🙂


I've stored an int value inside UserDefaults which is taken from the current device time + 1 hour. Then the check I want to perform is against the devices current time. So, if the current time is greater than the UserDefaults time then return true.


I've managed to add an hour to a let constant of date by the following: date.addingTimeInterval(3600) and then pulling out both the .hour and .minute and reassigning this to an Int. This should solve any daylight savings time issues etc, if I understood it correctly.


I've not thought about user changing the clock.


Hope that helps.

I've stored an int value inside UserDefaults which is taken from the current device time + 1 hour. Then the check I want to perform is against the devices current time.

Right. I understand what your code is doing. What I don’t understand is why it’s doing it. Without knowing more about your goạls, it’s hard to offer any concrete advice as to how you should proceed.

For example, it seems like you’re trying to detect hour changes. So, consider this thought experiment…

If it’s summer in Coolangatta — in Queensland, which doesn’t do DST — and I drive across the border to Tweed Heads — in New South Wales, which does — what do you want your app to do? Should it say it’s on a different hour because the local time has changed? Or say it’s on the same hour because an hour hasn’t passed?

How you answer this and the other questions I posed is critical to how you would write your code.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi,


I want to minimise API calls to 1 hour intervals. I have a button which gets the users current location and then feeds the latitude and longitude into an API and the API returns the data. The data changes every hour so I don't want the user to be hitting the button multiple times taxing the API resources when the data hasn't changed.


Is there a way to handle this without a checkTimer function?

If you’re just using this to the limit the rate at which you generate requests, you don’t need to mess around with calendars at all. You can grab the current time and compare it to the previous time. For example:

self.then = Date()

… time passes …

let now = Date()
if now > (then + 3600) {
    self.then = now
    … do stuff …
}
Date
counts in seconds since a reference date in UTC, so it’s unaffected by time zone, locale, and so on. Also, while it’s a bad idea to hard-code values like the number of seconds in a day, in this case the number of seconds in an hour is always 3600 (modulo leap seconds, which you can reasonably ignore).

The only gotcha here relates to the user changing the clock. If they set the clock forward that’s not a problem (you may run an unnecessary request, but it doesn’t sound like that’s a big deal) but if they set the clock backwards your requests could be disabled for a long time. You can avoid this by:

  1. Registering for the

    NSSystemClockDidChangeNotification
    notification
  2. If that fires, setting your

    self.then
    value to
    Date.distantPast
    so the next request always runs

Date.distantPast

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks! This solution works as I requested. I already had the NSSystemClockDidChangeNotification to handle this use case so bonus.