Search code examples
iosswifttimezonensdatenstimezone

Dealing with two timezones


I've been struggling with this issue since yesterday and I still lost. Date related stuff is really my bane in programming.

Let me explain the situation. I'm from Sri Lanka (GMT +5:30) and I'm working with a client in Sweden (GMT +1). I'm retrieving a JSON response from an API hosted there. Here's a stub of it. Each array of dictionary in the response is called a Handover.

[
  {
    "Tim" : 8,
    "Pat" : {
      "Id" : 5104
    },
    "Sta" : "Ej utfört",
    "SB" : 1066,
    "CB" : 0,
    "Date" : "2015-02-19T00:00:00+01:00",
    "DD" : null,
    "HTI" : 1
  },
  {
    "Tim" : 8,
    "Pat" : {
      "Id" : 5029
    },
    "Sta" : "",
    "SB" : null,
    "CB" : 0,
    "Date" : "2015-02-19T00:00:00+01:00",
    "DD" : null,
    "HTI" : 1
  }
]

The troublemaker here is that Date field. As you can see the date string is sent in ISO 8601 format. What I do from the app's side is create objects out of them and store it in core data. So in order to save this date value, first I use this library called ISO8601DateFormatter to convert the date string to NSDate. Below is the helper method I wrote for that.

public class func getDateFromISO8601DateString(dateString: String?) -> NSDate? {
    if let dateString = dateString {
        let formatter = ISO8601DateFormatter()
        formatter.defaultTimeZone = NSTimeZone.localTimeZone()
        formatter.includeTime = true
        let convertedDate = formatter.dateFromString(dateString)
        return convertedDate
    } else {
        return nil
    }
}

Now when I convert the date string 2015-02-19T00:00:00+01:00, I get this NSDate value, 2015-02-18 23:00:00 +0000.

Later in the app, I need to retrieve a set of handovers for the current date. Below is my the code I wrote for that.

let fetchRequest = NSFetchRequest()
let entityDescription = NSEntityDescription.entityForName("Handover", inManagedObjectContext: NSManagedObjectContext.MR_defaultContext())
let datePredicate = NSPredicate(format: "date > %@ AND date < %@", NSDate().beginningOfDay(), NSDate().endOfDay())

fetchRequest.entity = entityDescription
fetchRequest.predicate = datePredicate

var error: NSError?
let handovers = NSManagedObjectContext.MR_defaultContext().executeFetchRequest(fetchRequest, error: &error) as? [Handover]
return handovers

Here's another place where dates are used. To filter out records according to a date value, I needed to get the start time and the end time of the date. So I have these following methods to return those values. These methods were taken from this library.

func beginningOfDay() -> NSDate {
    let calendar = NSCalendar.currentCalendar()
    calendar.timeZone = NSTimeZone.localTimeZone()
    let components = calendar.components(.YearCalendarUnit | .MonthCalendarUnit | .DayCalendarUnit, fromDate: self)

    return calendar.dateFromComponents(components)!
}


func endOfDay() -> NSDate {
    let calendar = NSCalendar.currentCalendar()
    calendar.timeZone = NSTimeZone.localTimeZone()
    let components = NSDateComponents()
    components.day = 1

    return calendar.dateByAddingComponents(components, toDate: self.beginningOfDay(), options: .allZeros)!.dateByAddingTimeInterval(-1)
}

Here are the values I get from these methods.

beginningOfDay() - 2015-02-18 18:30:00 +0000

endOfDay() - 2015-02-19 18:29:59 +0000

Here's the crazy part. All this code works when I run the app from where I live. But when my client runs it, the handover fetching method returns zero results!

I have tracked the issue down and found out it's something wrong with dates and times. But I just can't figure out a way to correct it. Everywhere a date operation is done, I've set the timezone to localTimeZone(). In the AppDelegate's didFinishLaunchingWithOptions method, I also resets the timezone NSTimeZone.resetSystemTimeZone(). Nothing seems to work.

Anyone has any ideas/suggestions? I'd be more than grateful if you could help me out on this one.

Thank you.


Solution

  • The date "2015-02-19T00:00:00+01:00" is exactly the beginning of a day in the GMT+01 time zone, and therefore does not match the predicate

    NSPredicate(format: "date > %@ AND date < %@", NSDate().beginningOfDay(), NSDate().endOfDay())
    

    Replacing the first > by >= should solve the problem:

    NSPredicate(format: "date >= %@ AND date < %@", NSDate().beginningOfDay(), NSDate().endOfDay())
    

    There is also no need to subtract one second in your endOfDay() method, since you already compare the second date with <.