Ankit Srivastava
Ankit Srivastava

Reputation: 12405

How to calculate days difference between two NSDate objects in different time zones?

I have two NSDate which are initiated from UTC dates. Lets call them (A & B)

I know that the A represents a day in China and B represents a day in USA. (I know the time zones.) How can I calculate the difference in days between the two...?

I have been using the following method which is obviously incorrect.

class func daysDifferenceIn(firstDate: NSDate, firstTimeZone: String, secondDate: NSDate, secondTimeZone: String) -> Int {
    objc_sync_enter(self)

    let firstDateComponents = NSCalendar.CommonCalendar.componentsInTimeZone(NSTimeZone(name: firstTimeZone)!, fromDate: firstDate)
    let secondDateComponents = NSCalendar.CommonCalendar.componentsInTimeZone(NSTimeZone(name: secondTimeZone)!, fromDate: secondDate)
    NSCalendar.CommonCalendar.timeZone = NSTimeZone(name: firstTimeZone)!
    let firstCalDate = NSCalendar.CommonCalendar.dateFromComponents(firstDateComponents)
    NSCalendar.CommonCalendar.timeZone = NSTimeZone(name: secondTimeZone)!
    let secondCalDate = NSCalendar.CommonCalendar.dateFromComponents(secondDateComponents)
    objc_sync_exit(self)

    return firstCalDate!.numberOfDaysUntilDateTime(secondCalDate!)
}

func numberOfDaysUntilDateTime(toDateTime: NSDate, inTimeZone timeZone: NSTimeZone? = nil) -> Int {
    let calendar = NSCalendar.CommonCalendar
    if let timeZone = timeZone {
        calendar.timeZone = timeZone
    }

    var fromDate: NSDate?, toDate: NSDate?

    calendar.rangeOfUnit(.Day, startDate: &fromDate, interval: nil, forDate: self)
    calendar.rangeOfUnit(.Day, startDate: &toDate, interval: nil, forDate: toDateTime)

    let difference = calendar.components(.Day, fromDate: fromDate!, toDate: toDate!, options: [])

    return difference.day
}

I can manually subtract day components from firstDateComponents and secondDateComponents which I don't want to do as I have to look for edge cases of 31 and 28 and so on. Any help is appreciated. Thanks.

UPDATE

First date is 2017-02-10 16:15:00 +0000 Second date is 2017-02-11 03:20:00 +0000 Both are UTC.

firstTimeZone String "Asia/Shanghai" secondTimeZone String "America/Los_Angeles"

So the day difference is -1 Day. Basically I am implementing flight status and you can see the following link as the flight lands 1 day prior to take day. As it flies from West to East.

https://www.google.co.in/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=ua+890

Also a description of Date components.

Printing description of firstDateComponents:
<NSDateComponents: 0x600000142310>
    Calendar: <CFCalendar 0x60000088b6d0 [0x10c5d3df0]>{identifier = 'gregorian'}
    TimeZone: Asia/Shanghai (GMT+8) offset 28800
    Era: 1
    Calendar Year: 2017
    Month: 2
    Leap month: no
    Day: 11
    Hour: 0
    Minute: 15
    Second: 0
    Nanosecond: 0
    Quarter: 0
    Year for Week of Year: 2017
    Week of Year: 6
    Week of Month: 2
    Weekday: 7
    Weekday Ordinal: 2
Printing description of secondDateComponents:
<NSDateComponents: 0x60000014b8f0>
    Calendar: <CFCalendar 0x60000049b620 [0x10c5d3df0]>{identifier = 'gregorian'}
    TimeZone: America/Los_Angeles (PST) offset -28800
    Era: 1
    Calendar Year: 2017
    Month: 2
    Leap month: no
    Day: 10
    Hour: 19
    Minute: 20
    Second: 0
    Nanosecond: 0
    Quarter: 0
    Year for Week of Year: 2017
    Week of Year: 6
    Week of Month: 2
    Weekday: 6
    Weekday Ordinal: 2

Upvotes: 0

Views: 854

Answers (3)

Duncan C
Duncan C

Reputation: 131408

This is an odd case. You're looking for the difference in calendar dates between two Dates when those dates are evaluated in a specific time zone.

I did some playing, and came up with code that works for dates that fall in the same year:

let date = Date()

guard let nycTimeZone = TimeZone(abbreviation: "EST"),
  let nzTimeZone = TimeZone(abbreviation: "NZDT") else {
    fatalError()
}
var nycCalendar = Calendar(identifier: .gregorian)
nycCalendar.timeZone = nycTimeZone
var nzCalendar = Calendar(identifier: .gregorian)
nzCalendar.timeZone = nzTimeZone

let now = Date()

let nycDayOfYear = nycCalendar.ordinality(of: .day, in: .year, for: now)

var nzDayOfYear = nzCalendar.ordinality(of: .day, in: .year, for: now)

I'm using New York and Aukland, NZ as my time zones because as of the time of this writing, those zones are on different dates.

As of now (~12:00 PM on Feb 11, 2017 in US Eastern Standard Time (UTC - 5) the code above gives

nycDayOfYear = 42

and

nzDayOfYear = 43

It would take some work to make that calculation work across year boundaries.

Curiously, the following code:

var nzDayOfEra = nzCalendar.ordinality(of: .day, in: .era, for: now)
let nycDayOfEra = nycCalendar.ordinality(of: .day, in: .era, for: now)

Gives the same value for both NZ and NYC. I'm not sure why.

EDIT:

Ok, I did some experimenting and got code that works. What I do is to convert both dates to month/day/year date components using a calendar set to the local time zone for each time. Then I use a method dateComponents(_:from:to:) to calculate the difference between those 2 DateComponents, in days:

import UIKit

let date = Date()

guard let nycTimeZone = TimeZone(abbreviation: "EST"),
  let nzTimeZone = TimeZone(abbreviation: "NZDT") else {
    fatalError()
}
var nycCalendar = Calendar(identifier: .gregorian)
nycCalendar.timeZone = nycTimeZone
var nzCalendar = Calendar(identifier: .gregorian)
nzCalendar.timeZone = nzTimeZone

let now = Date()


let nycDateComponents = nycCalendar.dateComponents([.month, .day, .year], from: now)
let nzDateComponents = nzCalendar.dateComponents([.month, .day, .year], from: now)

let difference = Calendar.current.dateComponents([.day],
  from: nycDateComponents,
    to: nzDateComponents)

let daysDifference = difference.days

As of this writing that gives a daysDifference of 1. Since we're using the dateComponents(_:from:to:) function, it takes care of the math to calculate the number of days difference between the 2 month/day/year DateComponents.

Upvotes: 3

Sulthan
Sulthan

Reputation: 130092

A NSDate represents a moment in time. It has no time zone. Only string representations of dates have time zone information.

If you have the dates, just take the number of days between them. Don't worry about time zones.

Usually:

let difference = calendar.components(.Day, fromDate: self, toDate: toDate!, options: [])
return difference.day

should be enough.

Upvotes: 1

Stefan Stefanov
Stefan Stefanov

Reputation: 829

Have you tried using let interval = laterDate.timeIntervalSinceDate(earlierDate)? This will return the difference between the two dates in seconds.

Upvotes: 0

Related Questions