Last active
October 5, 2017 19:45
-
-
Save cmkilger/3c33b2f0d6e180ad4c8302e4bdb41e91 to your computer and use it in GitHub Desktop.
NSDateFormatter extension for localizing non-standard dateFormats.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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