Skip to content

Instantly share code, notes, and snippets.

@MattCheetham
Last active March 7, 2017 12:25
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MattCheetham/c717648e9604d19615d7ee89dbc695aa to your computer and use it in GitHub Desktop.
Save MattCheetham/c717648e9604d19615d7ee89dbc695aa to your computer and use it in GitHub Desktop.
Swift date formatting
//: Playground - noun: a place where people can play
import UIKit
/// We're working with appointments.
/// So I get this string from the server and in some places I need to display it in the users local timezone and in other places I need to display it as the time and date that it was booked
var serverTimeString = "2017-03-21T19:00:00-07:00"
let localisedDateFormatter = DateFormatter()
localisedDateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZZZ"
// Displays as March 22nd, 2am (GMT) - CORRECT
var date = localisedDateFormatter.date(from: serverTimeString)
// TO DO: Output date as March 21st, 7pm.
// ??
@tmdvs
Copy link

tmdvs commented Mar 7, 2017

Hmm does this work for you?

var serverTimeString = "2017-03-21T19:00:00-07:00"

let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

var date = formatter.date(from: serverTimeString)

@MattCheetham
Copy link
Author

@tmdvs That seems to spit out nil for me. I wonder if doing this in playgrounds makes a difference...

@jellybeansoup
Copy link

jellybeansoup commented Mar 7, 2017

IIRC, the dashes and colons in the dateFormat need to be escaped.

localisedDateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZZZZZ"

see listing 2 under "parsing date strings" on this page.

@MattCheetham
Copy link
Author

@jellybeansoup The parsing into the Date object works fine, adding those escapes gives the same result and continues to work.

The problem is I need to display it to the user as the -07:00 timezone but I can't see a way to create a TimeZone object with that data.

@tmdvs
Copy link

tmdvs commented Mar 7, 2017

@MattCheetham I'm looking at regex-ing the timezone element out and setting it on the formatter

@MattCheetham
Copy link
Author

@tmdvs Yeah that's kind of where I'm thinking of going but it feels overkill. I felt like there must have been a simpler way but maybe not. Various examples online of people using substrings to split out the time zone.

@liamnichols
Copy link

liamnichols commented Mar 7, 2017

That's not really how date formatting should work. I dunno the ins and outs of where the input date comes from but sounds like you want to discard the timezone completely and display it to the user as 2017-03-21T19:00:00 but nicely formatted?

Best way to do that would be to ignore the Z component from the input string and then make sure the api date formatter and the ui date formatter have matching timezones but to do that you will need to update your input value (get them to change the API or manipulate the string)

Something like this:

/// We're working with appointments.
/// So I get this string from the server and in some places I need to display it in the users local timezone and in other places I need to display it as the time and date that it was booked
let serverTimeString = "2017-03-21T19:00:00-07:00"

// TODO: ensure the string is at least 19 chars before manipulating
let manipulatedString = serverTimeString.substring(to: serverTimeString.index(serverTimeString.startIndex, offsetBy: 19))
let timeZone = TimeZone(secondsFromGMT: 0)

// A formatter with a FIXED locale for formatting the input date (from the API?)
let apiFormatter = DateFormatter()
apiFormatter.locale = Locale(identifier: "en_US_POSIX")
apiFormatter.timeZone = timeZone
apiFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"

// Ignores the concept of a time zone so formats as 19:00:00 GMT
let date = apiFormatter.date(from: manipulatedString)!

// A formatter for showing the date to the user
let uiFormatter = DateFormatter ()
uiFormatter.locale = Locale(identifier: "en_GB")
uiFormatter.timeZone = timeZone
uiFormatter.dateStyle = .medium
uiFormatter.timeStyle = .medium

let display = uiFormatter.string(from: date) // "21 Mar 2017, 19:00:00"

@tmdvs
Copy link

tmdvs commented Mar 7, 2017

@MattCheetham

This works, I get Tuesday, 21 March 2017 at 19:00:00 GMT-07:00

do {
            let serverTimeString = "2017-03-21T19:00:00-07:00"
            let pattern = "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}([\\d:\\-].*)"
            
            let rexp = try NSRegularExpression(pattern: pattern, options: .caseInsensitive)
            let matches = rexp.matches(in: serverTimeString, options: [], range: NSMakeRange(0,25))
            
            let timezone = (serverTimeString as NSString).substring(with: matches.last!.rangeAt(1)) as NSString
            let tzParts = timezone.components(separatedBy: ":")
            let hours = tzParts[0] as NSString
            let minutes = tzParts[1] as NSString
            
            let secondsSinceGMT = (hours.integerValue * 60 * 60) + (minutes.integerValue * 60)
            print(secondsSinceGMT)
            
            let formatter = DateFormatter()
            formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssxxxxx"
            formatter.timeZone = TimeZone(secondsFromGMT: secondsSinceGMT)
            
            let date = formatter.date(from: serverTimeString)
            
            let displayFormatter = DateFormatter()
            displayFormatter.dateStyle = .full
            displayFormatter.timeStyle = .full
            displayFormatter.timeZone = TimeZone(secondsFromGMT: secondsSinceGMT)
            
            print(displayFormatter.string(from: date!))
            
        } catch {
            // something borked
        }

@tmdvs
Copy link

tmdvs commented Mar 7, 2017

Could likely tidy it up. I've used regex and timezone calculations to try and catch any timezone not just -7 hours

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment