|Thursday, Nov 26, 2020||
EEEE, MMM d, yyyy
|Nov 26, 8:36 PM||
MMM d, h:mm a
|Nov 26, 2020||
MMM d, yyyy
|Thu, 26 Nov 2020 20:36:30 +0000||
E, d MMM yyyy HH:mm:ss Z
The following table’s sample column are mostly based on the time
December 14th, 2008 4:35 PM UTC.
|y||2008||Year, no padding|
|yy||08||Year, two digits (padding with a zero if necessary)|
|yyyy||2008||Year, minimum of four digits (padding with zeros if necessary)|
|Q||4||The quarter of the year. Use QQ if you want zero padding.|
|QQQ||Q4||Quarter including "Q"|
|QQQQ||4th quarter||Quarter spelled out|
|M||12||The numeric month of the year. A single M will use '1' for January.|
|MM||12||The numeric month of the year. A double M will use '01' for January.|
|MMM||Dec||The shorthand name of the month|
|MMMM||December||Full name of the month|
|MMMMM||D||Narrow name of the month|
|d||14||The day of the month. A single d will use 1 for January 1st.|
|dd||14||The day of the month. A double d will use 01 for January 1st.|
|F||3rd Tuesday in December||The day of week in the month|
|E||Tue||The abbreviation for the day of the week|
|EEEE||Tuesday||The wide name of the day of the week|
|EEEEE||T||The narrow day of week|
|EEEEEE||Tu||The short day of week|
|h||4||The 12-hour hour.|
|hh||04||The 12-hour hour padding with a zero if there is only 1 digit|
|H||16||The 24-hour hour.|
|HH||16||The 24-hour hour padding with a zero if there is only 1 digit.|
|a||PM||AM / PM for 12-hour time formats|
|m||35||The minute, with no padding for zeroes.|
|mm||35||The minute with zero padding.|
|s||8||The seconds, with no padding for zeroes.|
|ss||08||The seconds with zero padding.|
|zzz||CST||The 3 letter name of the time zone. Falls back to GMT-08:00 (hour offset) if the name is not known.|
|zzzz||Central Standard Time||The expanded time zone name, falls back to GMT-08:00 (hour offset) if name is not known.|
|ZZZZ||CST-06:00||Time zone with abbreviation and offset|
|Z||-0600||RFC 822 GMT format. Can also match a literal Z for Zulu (UTC) time.|
|ZZZZZ||-06:00||ISO 8601 time zone format|
|For the full reference of available format options, see Unicode Technical Reference #35.|
You should be aware that using a custom dateFormat comes with a risk of falling into some fallacies.
Especially, you need to realize that the user can use different Locales, and that date formats are different for different locales, regions and user settings. For example one date formatted with a dateFormat that fits the US might become confusing for anyone in Europe.
Apple already has a dedicated paragraph in their documentation about best practices for formatting a date to present to the user in a locale-aware way. Below is just the TL;DR.
The main recommendation to follow is to prefer using dateStyle and timeStyle over dateFormat.
This is because those are locale-aware and account for a lot of edge cases that you can otherwise miss when using a custom and hardcoded dateFormat.
I would even advise you to convince your designer against using a custom format and explain them that Date and Time is a tricky subject with lots of edge cases and that it's generally not worth using a custom format that might fit their assumptions about the locale and region they're used to use but might not fit a lot of other's.
If you still need to use a custom dateFormat, be sure that you use dateFormatter.setLocalizedDateFormatFromTemplate(…) or dateFormatter.dateFormat = DateFormatter.dateFormat(fromTemplate:…options:…locale:…) instead of setting it to a hard-coded String.
Apple Documentation also has a dedicated section about those cases here.
If you need to parse ISO8601 dates, consider using ISODateFormatter instead of a DateFormatter with a custom dateFormat.
This class is dedicated to handle the ISO8601 standard and all its possible variants and edge cases better than using a custom dateFormat would.
Tip: One of the little-known options of ISO8601DateFormatter is that it is also able to handle fractional seconds if you set it up using formatter.formatOptions.insert(.withFractionalSeconds).
In last resort, if you need to parse a date for an API that doesn’t fall into ISO8601 format, but that also isn’t intended for UI (and should thus not depend on the user’s locale/region/language), then that is the only case when you can use a fixed string as a value for dateFormatter.dateFormat.
BUT then ALWAYS also set your dateFormatter.locale to Locale(identifier: "en_US_POSIX") on your DateFormatter.
en_US_POSIX is a special locale that guarantees that the formatting and parsing won’t depend on the phone’s locale, and is designed exactly for parsing those “internet dates with fixed format”.
If you don't force the locale to en_US_POSIX, there are risks that your code might seem to work in some regions (like the US), but will fail to parse your API responses if the user has its phone set in another region (e.g. en_GB, en_ES or fr_FR), where date formatting is different, or use 12-hour time and not 24-hour time.
You can test this kind of edge case on device by setting your phone settings to use en_ES for example and set it to use 12-hour with am/pm, and try to parse a date like 2020-01-15T22:00:00Z.
For more information about those commonly overlooked cases, you can read Apple's TN1480.
This section was contributed by Olivier Halligon
nsdateformatter.com is written with Swift 4.2, as a means to learn open-source Swift, the Swift Package Manager, and deploying to Linux. It uses the Vapor web application framework and deployed is to Heroku.
The site is open source, so if you want to fix a bug or submit an enhancement, feel free to submit a pull request.
Want to see how to use
DateFormatter in Swift?
Check out this free screencast: