Search code examples
swiftnsdatecomponents

DateComponents giving wrong hour


I am trying to implement a simple countdown timer in my test app.

I have two dates:

  1. fromDate - which is current time that I get by Date(), e.g. 2021-08-27 11:07:34 +0000
  2. toDate - is a future date, e.g. 2021-11-17 01:00:00 +0000

I am using DateComponents to get back the difference in days, hours, minutes and seconds.

let components = Calendar.current.dateComponents([.day, .hour, .minute, .second],
                                                 from: fromDate,
                                                 to: toDate)

Its returning me back the values for days hours minute and second 81, 12, 52, 25

The values for day, minute and second are correct, but the hour is 1 hour less.

I suspect daylight timing has to do something with this but I cannot find anything that can help here.

Kindly help me what I am doing wrong as I have tried many things in past few days but nothing seems to work


Solution

  • I was able to reproduce the behaviour by using:

    let from = Date(timeIntervalSince1970: 1630062455)
    print(from) // 2021-08-27 11:07:35 +0000
    let to = Date(timeIntervalSince1970: 1637110800)
    print(to) // 2021-11-17 01:00:00 +0000
    var calendar = Calendar(identifier: .gregorian)
    calendar.timeZone = TimeZone(identifier: "Europe/London")!
    let comp = calendar.dateComponents([.day, .hour, .minute, .second], from: from, to: to)
    print(comp.day!, comp.hour!, comp.minute!, comp.second!)
    

    The reason why this happens is because when doing dateComponents(_:from:to:), Calendar takes into account its timezone. After all, without a timezone, (almost) no date components would make sense - you would not be able to tell what hour a Date is, for example. A Date just represents an instant in time/n seconds since the epoch.

    (In the case of Calendar.current, the timezone it uses is TimeZone.current)

    Europe/London would go out of DST at some point between from and to. This means the calendar would calculate the difference in date components between:

    from: 2021-08-27 12:07:35
    to:   2021-11-17 01:00:00
    

    Notice that the first time is 12:07:35, rather than 11:07:35. This is because at 2021-08-27 11:07:35 +0000, the local date time at Europe/London really is 2021-08-27 12:07:35.

    To get your desired output, just change the calendar's timeZone to UTC:

    var calendar = Calendar.current
    calendar.timeZone = TimeZone(identifier: "UTC")!
    let comp = calendar.dateComponents([.day, .hour, .minute, .second], from: from, to: to)