This is a very puzzling situation, I've read lots of documentation on the NSDateFormatter but I can't seem to be able to set the localized date format template. Using the dateFormat
property works but using setLocalizedDateFormatFromTemplate
does not, essentially I have the following code in swift 5.3:
First in a terminal open a swift REPL and type the following:
import Foundation // takes a few secs
var ftt = DateFormatter()
ftt.locale = Locale(identifier: "en_US")
ftt.setLocalizedDateFormatFromTemplate("'Deliver on' MMMM d 'at' h:mm a 'sharp'")
After running I get the following output:
ftt: DateFormatter = {
baseNSFormatter@0 = {
baseNSObject@0 = {
isa = NSDateFormatter
}
}
_attributes = 3 key/value pairs {
[0] = {
key = "locale"
value =
}
[1] = {
key = "formatterBehavior"
value = Int64(1040)
}
[2] = {
key = "dateFormat"
value = "MMMM d, h:mm a"
}
}
_formatter = {}
_counter = 0
_cacheGeneration = 3
_behavior = 0
}
As you can see from the output neither the locale
or the dateFormat
have been stored. Formatting a date results in the following:
14> ftt.string(from: Date())
$R1: String = "October 21, 8:19 PM"
I've made sure that the locale identifier is correct, I've followed a few tutorials on DateFormatter such as:
And checked usage of the setLocalizedDateFormatFromTemplate
as well as Apple's documentation and make sure to call it after setting locale
.
If I assign the dateFormat
property directly I get the desired result:
17> ftt.dateFormat = "'Deliver on' MMMM d 'at' h:mm a 'sharp'"
ftt: DateFormatter = {
baseNSFormatter@0 = {
baseNSObject@0 = {
isa = NSDateFormatter
}
}
_attributes = 3 key/value pairs {
[0] = {
key = "locale"
value =
}
[1] = {
key = "formatterBehavior"
value = Int64(1040)
}
[2] = {
key = "dateFormat"
value = "\'Deliver on\' MMMM d \'at\' h:mm a \'sharp\'"
}
}
_formatter =
_counter = 0
_cacheGeneration = 2
_behavior = 0
}
18> ftt.string(from: Date())
$R2: String = "Deliver on October 21 at 8:25 PM sharp"
What's going on?! am I missing something obvious? I want to understand the behavior.
Thanks in advance!
I think you misunderstand what setLocalizedDateFormatFromTemplate
does. Its documentation says:
Calling this method is equivalent to, but not necessarily implemented as, setting the
dateFormat
property to the result of calling thedateFormat(fromTemplate:options:locale:)
method, passing no options and the locale property value.
Now what does dateFormat(fromTemplate:options:locale:)
do? Lets see:
Return Value
A localized date format string representing the date format components given in template, arranged appropriately for the locale specified by locale.
The returned string may not contain exactly those components given in template, but may—for example—have locale-specific adjustments applied.
So dateFormat(fromTemplate:options:locale:)
tries to localise the template to a specified locale. If no locale is specified, it uses Locale.current
. For example:
// this produces "MM/dd/yyyy"
DateFormatter.dateFormat(fromTemplate: "yyyy dd MM", options: 0, locale: Locale(identifier: "en-US"))
That explains why it removes all the quoted strings in your format, because the localisation engine doesn't recognise your quoted strings, so to produce a "localised" version of your date format, the best it can do is to remove them. As far as it is concerned, the quoted strings could be in a different language!
So it's not that setLocalizedDateFormatFromTemplate
did not change dateFormat
. It did change it to "MMMM d, h:mm a"
, which is what iOS thinks is the best "localised" version of the format
"'Deliver on' MMMM d 'at' h:mm a 'sharp'"
In this situation, you should set dateFormat
directly, rather than setLocalizedDateFormatFromTemplate
, as you don't want a localised date format.