Search code examples
iosswiftnsdateformatternslocale

How to replace date component separators with other symbols?


In my app I would like to allow users to select a variety of date formats that suit their preferences for displaying a date. However, I don't want to define a fixed date format, so instead I'm getting the most appropriate format for different components based on the current locale. I understand different locals may use different symbols to separate the components, but I am wondering if it would be possible to set those separator symbols while preserving the correct order of the components as appropriate for that locale.

For example, in the US the following code returns "09/06/2014" but in the UK it would be "06/09/2014", and I want to preserve that order but replace the slash with dashes or spaces. However I don't believe I can simply parse the returned String and replace instances of '/' with other characters because in some locales they probably don't use '/' (I'm not certain but it seems very likely).

NSDateFormatter.dateFormatFromTemplate("MMddyyyy", options: 0, locale: NSLocale.currentLocale())

Is it possible to change the date component separator character when at the same time getting the most appropriate format for the current locale?


Solution

  • I went through all the available locales and checked out the short date format to determine that all languages use one of these three delimiters as of now: / . -

    So to allow custom delimiters while preserving the format, I just replace those characters with the custom character. For example:

    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate: "MMddyyyy", options: 0, locale: .current)
    
    var output = dateFormatter.string(from: .now)
    output = output.replacingOccurrences(of: "/", with: " ")
    output = output.replacingOccurrences(of: ".", with: " ")
    output = output.replacingOccurrences(of: "-", with: " ")
    

    I'd be nice to find a better solution.

    Here’s the code I used to make this determination. The set of delimiters it ultimately creates contains more than the actual delimiters, as some date components use non-numbers yet aren’t delimiters. Also note the ar_SA locale is unique as it has one instance of a character you may consider a delimiter, the Arabic comma, but you likely don’t want to replace that with a custom delimiter given its representation.

    var delimitersSet = Set<Character>()
                
    for identifier in Locale.availableIdentifiers {
        let locale = Locale(identifier: identifier)
                    
        let dateFormatter = DateFormatter()
        dateFormatter.locale = locale
        dateFormatter.dateStyle = .short
                    
        let shortDateFormat = dateFormatter.string(from: .now)
        let delimiters = Set(shortDateFormat.filter { !$0.isNumber })
        print("\(identifier): \(shortDateFormat): \(delimiters)")
    
        for delimiter in delimiters {
            if !delimitersSet.contains(delimiter) {
                print("Unique \(delimiter)")
            }
        }
        delimitersSet.formUnion(delimiters)
    }
                
    print("All possible date delimiters: \(delimitersSet)")