3

Is there a way to get a server time to use with swift. I want to set a static time based on a server. so that even if a user changes the time zone and the date, it wouldn't matter and the time which will show on the view controller will be a time based on the server rather than the NSDate

 let today = NSDate()
 println(today)

at present the above code, changes the date and time if its altered in the phone settings. i am also using parse in my code. is there a way to get the server time from parse.

  • Why not just set the `NSDate`'s timezone to the same as the server you want to use, therefore no network call? http://stackoverflow.com/a/24917928/1186243 – sbarow Jul 13 '15 at 10:35
  • the timezone function is fine based on the link. how do you set the date to the server is what i would like to know. cause at the moment when i change the date setting on my phone to a past or future date, the date changes in the app :) – Mugunthan Balakrishnan Jul 13 '15 at 11:10
  • Have a look at this https://www.parse.com/questions/is-there-a-way-to-retrieve-the-current-server-time – sbarow Jul 13 '15 at 11:17
  • think that code is javascript based – Mugunthan Balakrishnan Jul 13 '15 at 11:26
  • 1
    Compare http://stackoverflow.com/questions/9075303/get-date-and-time-from-apple-server. – Martin R Jul 13 '15 at 11:36
  • @MugunthanBalakrishnan that code is Javascript, because you mentioned you want to use Parse, so you would use Parse's cloud code which is written in Javascript. I would suggest using Martin R's link if you just want to call a service that already exists. – sbarow Jul 13 '15 at 11:40
  • thanks guys, will try and work around the link martin has suggested – Mugunthan Balakrishnan Jul 13 '15 at 11:44
  • https://github.com/freak4pc/NSDate-ServerDate i came across this example, its written in Objective-C. tried it and it works . should be able to convert it to swift. – Mugunthan Balakrishnan Jul 13 '15 at 11:53

3 Answers3

6

Based on this link https://github.com/freak4pc/NSDate-ServerDate i wrote the code for swift. Tried and tested. And it works.

override func viewDidLoad() {
    super.viewDidLoad()

    //call ServerTimeReturn function

    serverTimeReturn { (getResDate) -> Void in
        var dFormatter = NSDateFormatter()
        dFormatter.dateStyle = NSDateFormatterStyle.LongStyle
        dFormatter.timeStyle = NSDateFormatterStyle.LongStyle
        dFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
        var dateGet = dFormatter.stringFromDate(getResDate)

        println("Formatted Time : \(dateGet)")
    }

        }

    func serverTimeReturn(completionHandler:(getResDate: NSDate!) -> Void){

    let url = NSURL(string: "http://www.google.com")      
    let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
        let httpResponse = response as? NSHTTPURLResponse
        if let contentType = httpResponse!.allHeaderFields["Date"] as? String {

            var dFormatter = NSDateFormatter()
            dFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
            var serverTime = dFormatter.dateFromString(contentType)
            completionHandler(getResDate: serverTime)
        }
    }

    task.resume()
}
5

Since network calls are expensive,
you shouldn't make the same network call every time you need to get corrected datetime.

When getting time from some server, store the time difference between local and the server somewhere and use it to get corrected date.

example

private var timeGap: TimeInterval = 0
private var referenceDate = Date() {
    didSet { timeGap = Date().distance(to: referenceDate) }
}

var correctedDate: Date {
    let possibleDate = Date()
    let ignorableNetworkDelay: TimeInterval = 2

    guard abs(timeGap) > ignorableNetworkDelay else { return possibleDate }
    return possibleDate.advanced(by: timeGap)
}

func setReferenceDate() {
    let url = URL(string: "https://www.google.com")

    URLSession.shared.dataTask(with: url!) { _, response, _ in
        let httpResponse = response as? HTTPURLResponse
        if let stringDate = httpResponse?.allHeaderFields["Date"] as? String {
            let formatter = DateFormatter()
            formatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
            formatter.timeZone = TimeZone.current
            serverDate = formatter.date(from: stringDate) ?? Date()
        } else {
            print("getting reference date from web failed")
            referenceDate = Date()
        }
    }.resume()
}

This way, I can refer to correctedDate any time without worrying about the cost,
and call setReferenceDate() only when I need to correct datetime.

(for instance, when the app goes into background)

eastriver lee
  • 173
  • 2
  • 9
4
  func getServerTimer(completion:@escaping (Date?)->()) {

    serverTimeReturn { date   -> Void in
        let dFormatter = DateFormatter()
        dFormatter.dateStyle = .long
        dFormatter.timeStyle = .long
        dFormatter.timeZone = TimeZone(abbreviation: "GMT")
        if let date = date {
            let dateGet = dFormatter.string(from: date)
            completion(date)
            print("Formatted Time : \(dateGet)")
        } else {
            completion(nil)
        }
    }

}

func serverTimeReturn(completionHandler:@escaping (_ getResDate: Date?) -> Void) {

    let url = NSURL(string: "https://www.google.com")
    let task = URLSession.shared.dataTask(with: url! as URL) {(data, response, error) in
        let httpResponse = response as? HTTPURLResponse
        if let contentType = httpResponse!.allHeaderFields["Date"] as? String {

            let dFormatter = DateFormatter()
            dFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z"
            dFormatter.locale = Locale(identifier: "en-US")
            if let serverTime = dFormatter.date(from: contentType) {
                completionHandler(serverTime)
            } else {
                completionHandler(nil)
            }
        }
    }

    task.resume()
}
Masoud Rahimi
  • 5,785
  • 15
  • 39
  • 67
Chris
  • 7,579
  • 3
  • 18
  • 38