Swift Date function do not work as Expected

1.1k views Asked by At

I am trying to find out the difference between two date in seconds using Swift 4.1. This is the code I use,

func getDurationInSeconds(date1 :Date) -> Int{
    guard let durationInSeconds = Calendar.current.dateComponents([.second], from: Date(), to: date1).second else {
            return 0
    }
    return durationInSeconds
}

Function to generate date1 from 2018-10-09T18:19:00Z

func dateFromString(stringDate:String) -> Date? {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
        dateFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale?
        let date = dateFormatter.date(from: stringDate)
        return date
    }

The Date is always returning back an hour less than my current device time, so the calculation is not working as expected. If my current device time is 16:34 the Date() function returns it back as 15:34.

I have seen that Date() is returning back the time in UTC not based on my timezone.

At the moment if I pass in a Date 09/10/2018 14:25:00 and the current device time is 09/10/2018 14:20:00. I am expecting this function to return a value 300 which is 60 * 5 minute difference between two dates.

But I am getting back a value of 3900 which is because the date function returns the date as

09/10/2018 13:20:00 instead of 14:20

. So the duration will be 1 hour + the 300 second difference.

Including a sample output from Xcode console, my device time when I executed this code was 2018-10-09 17:56:28.565423

(lldb) po date1
▿ 2018-10-09 17:59:00 +0000
  - timeIntervalSinceReferenceDate : 560800740.0

(lldb) po durationInSeconds
3731

(lldb) po Date()
▿ 2018-10-09 16:57:04 +0000
  - timeIntervalSinceReferenceDate : 560797024.35021996

(lldb)

But I cant find a proper way to find the correct duration between two times based on my current time zone. How can I do it?

2

There are 2 answers

13
ielyamani On

Since you are using a string that represents the current time in your time zone, try this instead:

func getDurationInSeconds(date1: Date) -> Int {
    return Int(-date1.timeIntervalSinceNow + TimeZone.current.daylightSavingTimeOffset(for: date1))
}

It uses this property and this method.

Or if you'd like to account for the time zone difference from UTC in dateFromString(stringDate:):

func getDurationInSeconds(date1: Date) -> Int {
    return Int(-date1.timeIntervalSinceNow)
}

func dateFromString(stringDate:String) -> Date? {
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
    dateFormatter.locale = Locale(identifier: "en_US_POSIX")
    let date = dateFormatter.date(from: stringDate)! //I am force-unwrapping for brevity
    let adjustedDate = date.addingTimeInterval(-TimeZone.current.daylightSavingTimeOffset(for: date))
    return adjustedDate
}

Test

I am in a UTC + 1h timezone:

let str = "2018-10-09T19:50:00Z"    //"2018-10-09T19:50:00Z"
let date1 = dateFromString(stringDate: str)! //"Oct 9, 2018 at 7:50 PM"
Date()         //At the time of testing this it is "Oct 9, 2018 at 7:53 PM"
getDurationInSeconds(date1: date1)  //213, which is 3 minutes and 33 seconds
0
D V On

The issue is not with the Date() returning wrong time. Date() always returns the current time, which is not really based on your (any other) local timezone.

The problem seems to be with the dateFormatter that you are using to generate the Date object from the date string.

Please try using the following lines of code:

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"

// Not necessary as the dateFormatter would take the device's timeZone to be the default.
dateFormatter.timeZone = Calendar.current.timeZone   

instead of:

dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"

The problem with the latter is that, you are specifying 'Z' to be the zero-offset time zone (UTC). So, the difference of 1 hour from UTC in your device is causing this offset.

And, while passing in the date string, please make sure that you skip the 'Z' at the end (For example, it should be like 2018-10-09T18:19:00).

The updated code should work good for you, and return the expected difference in seconds.