How to convert date like \/Date(1440156888750-0700)\/ to something that Swift can handle?

(The previous version of this answer was actually wrong, it did not handle the time zone correctly.)

According to Stand-Alone JSON Serialization:

DateTime values appear as JSON strings in the form of "/Date(700000+0500)/", where the first number (700000 in the example provided) is the number of milliseconds in the GMT time zone, regular (non-daylight savings) time since midnight, January 1, 1970. The number may be negative to represent earlier times. The part that consists of "+0500" in the example is optional and indicates that the time is of the Local kind - that is, should be converted to the local time zone on deserialization. If it is absent, the time is deserialized as Utc. The actual number ("0500" in this example) and its sign (+ or -) are ignored.

and Use JSON.NET to parse json date of format Date(epochTime-offset)

... In this [screwy format][1], the timestamp portion is still based solely on UTC. The offset is extra information. It doesn't change the timestamp. You can give a different offset, or omit it entirely and it's still the same moment in time.

the first number in \/Date(1440156888750-0700)\/ is the number of milliseconds since the "epoch" January 1, 1970 GMT, and the time zone part -0700 must simply be ignored.

Here is a Swift 5 extension method for Date which checks the validity of the string with a regular expression (accepting both \/Date(...)\/ and /Date(...)/, with or without a time zone specification) and converts the given number of milliseconds to a Date:

extension Date {
    init?(jsonDate: String) {

        let pattern = #"\\?/Date\((\d+)([+-]\d{4})?\)\\?/"#
        let regex = try! NSRegularExpression(pattern: pattern)
        guard let match = regex.firstMatch(in: jsonDate, range: NSRange(jsonDate.startIndex..., in: jsonDate)) else {
            return nil
        }

        // Extract milliseconds:
        let dateString = jsonDate[Range(match.range(at: 1), in: jsonDate)!]
        // Convert to UNIX timestamp in seconds:
        let timeStamp = Double(dateString)! / 1000.0
        // Create Date from timestamp:
        self.init(timeIntervalSince1970: timeStamp)
    }
}

Example:

let jsonDate = "\\/Date(1440156888750-0700)\\/"
print("JSON Date:", jsonDate)

if let theDate = Date(jsonDate: jsonDate) {
    print("Date:", theDate)
} else {
    print("wrong format")
}

Output:

JSON Date: \/Date(1440156888750-0700)\/
Date: 2015-08-21 11:34:48 +0000

(Versions for Swift 3 and Swift 4 can be found in the edit history.)


After a bit of experimenting around I came up with the following solution:

let dx = "/Date(1440156888750-0700)/"
let timestamp = (dx as NSString).substringWithRange(NSRange(location: 6,length: 13))
let timezone = (dx as NSString).substringWithRange(NSRange(location: 19,length: 5))
let dateIntermediate = NSDate(timeIntervalSince1970: Double(timestamp)! / 1000)

let outp = NSDateFormatter()
outp.dateFormat = "dd.MM.yyyy hh:mm::ssSSS"
outp.timeZone = NSTimeZone(forSecondsFromGMT: 0)

let input = outp.stringFromDate(dateIntermediate) + " " + timezone
let inp = NSDateFormatter()
inp.dateFormat = "dd.MM.yyyy hh:mm::ssSSS Z"

let finalDate = inp.dateFromString(input)
print(finalDate)

Let me explain:

  • we extract the millisecond timestamp and the timezone from the original string
  • we create a date from the timestamp to be able to split it into its different components
  • we output that date in a more standard way (not as timestamp) and append the previously extracted timezone to that string
  • we then read that string and parse a date from it again

Note
As @Phoen1xUK mentioned the timestamp might have a different length than 13 digits. You can handle that situation by stripping the /Date( and )/ and then splitting the string before the - (or +).