Skip to content

Instantly share code, notes, and snippets.

@cmkilger
Last active October 5, 2017 19:45
Show Gist options
  • Save cmkilger/3c33b2f0d6e180ad4c8302e4bdb41e91 to your computer and use it in GitHub Desktop.
Save cmkilger/3c33b2f0d6e180ad4c8302e4bdb41e91 to your computer and use it in GitHub Desktop.
NSDateFormatter extension for localizing non-standard dateFormats.
import Foundation
/// Removes key/value pairs from the format dictionary where another pair covers it.
///
/// The format dictionary should have locale identifiers as the keys, where language, script and country codes are separated by underscores (_), and the date format as the value.
///
/// Example: Given the following dictionary: ["en": "MMM d", "en_US": "MMM d"], "en_US" is unnessary because "en" (the language without the country) has the same format.
///
/// - parameter formats: The format dictionary to be reduced.
/// - parameter defaultFormat: A format that can be removed from the dictionary because it is the default. Defaults to `nil`.
/// - returns: The reduced format dictionary.
private func reducedFormatsFromFormats(formats: [String:String], defaultFormat: String? = nil) -> [String:String] {
var final = [String:String]()
for (key, format) in formats {
guard format != defaultFormat else { continue }
let components = key.components(separatedBy: "_")
if components.count == 3 {
let superKey = "\(components[0])_\(components[1])"
if format != formats[superKey] {
final[key] = format
}
} else if components.count == 2 {
let superKey = components[0]
if format != formats[superKey] {
final[key] = format
}
} else {
final[key] = format
}
}
return final
}
/// Prints Swift code for the dictionary.
///
/// - parameter formats: The dictionary.
private func printFormats(formats: [String:String]) {
print("let formats = [")
for (key, format) in formats.sorted(by: { $0.1 < $1.1 }) {
print("\t\"\(key)\": \"\(format)\",")
}
print("]")
}
private var cachedFormatters = [String:DateFormatter]()
extension DateFormatter {
/// Creates an `DateFormatter` using a format from *formats* that coincides with *locale*.
///
/// - Parameters:
/// - formats: A dictionary of [<locale>:<format>].
/// - defaultFormat: The default format to use if one isn't found in *formats*.
/// - locale: The locale with which to format the date.
/// - cacheKey: A key to use to cache the formatter. Should be unique for the given formats.
/// - Returns: A date formatter configured to the locale and appropriate format.
class func from(formats: [String:String], defaultFormat: String, locale: Locale = Locale.current, cacheKey: String) -> DateFormatter {
// Get the locale ID and check the cached formatters
let localeCacheKey = locale.identifier + "|" + cacheKey
if let formatter = cachedFormatters[localeCacheKey] {
return formatter
}
// Get the language, script, and country codes of the locale
let language = locale.languageCode!
let script = locale.scriptCode
let region = locale.regionCode
// Create an array of possible locale identifiers in the order of preference
var codes = [String]()
if let script = script, let country = region {
codes.append("\(language)_\(script)_\(country)")
}
if let region = region {
codes.append("\(language)_\(region)")
}
if let script = script {
codes.append("\(language)_\(script)")
}
codes.append(language)
// Get a format from the dictionary
var dateFormat = defaultFormat
for code in codes {
if let format = formats[code] {
dateFormat = format
break
}
}
// Create a formatter
let formatter = DateFormatter()
formatter.locale = locale
formatter.dateFormat = dateFormat
cachedFormatters[localeCacheKey] = formatter
return formatter
}
/// Returns an `DateFormatter` that shows the date as a short month and the day, e.g. Apr 2.
///
/// This method will cache the date formatter so that calling it more than once will return the same one.
/// Changes made to it will affect later calls.
///
/// - parameter locale: The locale the date should be formatted in. Defaults to the current locale.
/// - returns: The date formatter.
class func forShortMonthAndDate(using locale: Locale = Locale.current) -> DateFormatter {
let shortMonthAndDateFormats = [
"af": "dd MMM", "ak": "MMM d", "as": "MMM d", "bo": "MMMཚེས་d", "br": "MMM d", "brx": "MMM d", "bs": "dd. MMM", "chr": "MMM d", "ckb": "MMM d",
"cs": "d. MMM", "da": "d. MMM", "de": "d. MMM", "dsb": "d. MMM", "dz": "ཟླ་MMM ཚེས་dd", "ee": "MMM d", "en": "MMM d", "en_150": "dd MMM", "en_AD": "dd MMM",
"en_AL": "dd MMM", "en_AT": "dd MMM", "en_BA": "dd MMM", "en_BE": "dd MMM", "en_BW": "dd MMM", "en_BZ": "dd MMM", "en_CH": "dd MMM", "en_CY": "dd MMM",
"en_CZ": "dd MMM", "en_DE": "dd MMM", "en_DK": "dd MMM", "en_EE": "dd MMM", "en_ES": "dd MMM", "en_FI": "dd MMM", "en_FR": "dd MMM", "en_GR": "dd MMM",
"en_HR": "dd MMM", "en_HU": "dd MMM", "en_IS": "dd MMM", "en_IT": "dd MMM", "en_LT": "dd MMM", "en_LU": "dd MMM", "en_LV": "dd MMM", "en_ME": "dd MMM",
"en_MT": "dd MMM", "en_NL": "dd MMM", "en_NO": "dd MMM", "en_PL": "dd MMM", "en_PT": "dd MMM", "en_RO": "dd MMM", "en_RU": "dd MMM", "en_SE": "dd MMM",
"en_SI": "dd MMM", "en_SK": "dd MMM", "en_TR": "dd MMM", "en_ZA": "dd MMM", "en_ZW": "dd MMM", "eo": "MMM-dd", "es_CL": "dd-MM", "es_CO": "d/MM",
"es_GT": "d/MM", "es_MX": "dd/MM", "es_PA": "MM/dd", "es_PR": "MM/dd", "es_US": "MMM d", "et": "d. MMM", "eu": "MMM d", "fi": "d. MMM", "fil": "MMM d",
"fo": "d. MMM", "fur": "dd/MM", "gsw": "dd.MM", "gv": "MMM d", "he": "d בMMM", "hi": "dd/MM", "hr": "d. MMM", "hsb": "d. MMM", "hu": "MMM d.",
"hy": "dd MMM", "ii": "MMM d", "is": "d. MMM", "it": "dd MMM", "iu": "MMM d", "ja": "M月d日", "jgo": "MMM d", "kl": "MMM d", "kn": "MMM d",
"ko": "M월 d일", "kok": "MMM d", "ks": "MMM d", "ksh": "d. MMM", "kw": "MMM d", "ky": "MMM d", "lb": "d. MMM", "lkt": "MMM d", "lt": "MM-dd",
"lv": "d. MMM", "mgo": "MMM d", "mk": "dd MMM", "ml": "MMM d", "mn": "MMM d", "mt": "dd MMM", "nb": "d. MMM", "ne": "MMM d", "nn": "d. MMM",
"om": "dd MMM", "os": "dd MMM", "pt": "dd/MM", "qu": "MMM d", "rm": "dd-MM", "rw": "MMM d", "sah": "MMM d", "se": "MMM d", "seh": "dd/MM",
"si": "MMM d", "sk": "d. MMM", "sl": "d. MMM", "smn": "MMM d", "so": "dd-MMM", "sr": "dd. MMM", "tg": "MMM d", "ti": "dd MMM", "ug": "MMM d",
"uz": "MMM d", "vi": "dd-MM", "wae": "d. MMM", "zh": "M月d日", "zu": "MMM d",
]
return from(formats: shortMonthAndDateFormats, defaultFormat: "d MMM", locale: locale, cacheKey: "shortMonthAndDateFormat")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment