DateFormatter Date and Time Cheatsheet
A cheatsheet and a manager?
There is little that annoys me as much as the way that dates require a dateformatter that just hangs around in the parent class. There must be a better way!
Also the formats:
ddd mean, what exactly? Perhaps there is a way to actually let Swift help us out and provide readable code?
Indeed, and this article is it.
Difficulty: Beginner | Easy | Normal | Challenging
- Be able to code Swift using Playgrounds
Date: A specific point in time, independent of any calendar or time zone
DateFormatter: A formatter that converts between dates and their textual representations
DataComponents: A date or time specified in terms of units (such as year, month, day, hour, and minute) to be evaluated in a calendar system and time zone
The relevant objects
struct that represents a particular point in time. Now this does not relate to a time zone, rather it is a particular date.
Now — getting back to the link with
Date automatically bridges to the latter so…we kind of don’t need to worry about this.
Date is simply a moment in time, it doesn’t do anything interesting or fantastic with Strings (sorry). That comes with the…
Class that lets us move from dates to textual representations of those dates. That means you can represent your point in time as an easy-to-read String that might be useful to humans using your App.
More on this later! One thing that might be important though is that we are converting a Date which is ignorant of your calendar and timezone into something readable by humans, who are invariably interested in a calendar and timezone.
Struct that is the various parts of a date (year, hour etc.) for a calendar and time-zone.
We can retrieve the current system time as a
Date in one line. However, since we are dealing with Date objects here the representation is rather arbitrary
let now: Date = Date()
This gives the following output (if you are writing this code now, before I’ve published it. Which is impressive. In any other case the time (and probably date) will be different).
Apr 30, 2020 at 2:34 PM
2020-04-30 13:34:39 +0000
Which is a problem. My Mac says it’s 14:34 so…wait, what?
The answer is formatting. The time might is 13:34 in GMT but I’m in the UK so my calendar is set at GMT +1.
What good is a time and date if you don’t know where you are?
Well, that sounds like a case for UTC but that isn’t what this article is about so I’ll ignore your impetulant behaviour.
So we have the time now, and we want to display it in nice human-readable form.
Isn’t there a way to print the full date, like we might expect. Yes, yes there is. For this we can use DateFormatter
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .full
dateFormatter.timeStyle = .full
let dateString = dateFormatter.string(from: now)
That final print, up there? It writes
Thursday, April 30, 2020 at 2:42:19 PM British Summer Time to the console (once again, you might not be reading this now, and might not be in the UK. But you might be, I don’t know).
DateFormatter, perhaps surprisingly, allows you to format dates. This means that we can use
readableDateFormatter.dateFormat to specify our date format.
If you don’t give the date format a style OR a format? It just won’t display.
let readableDateFormatter = DateFormatter()
readableDateFormatter.dateFormat = "M-dd-yyyy HH:mm"
let readableDateString = readableDateFormatter.string(from: now)
Which on my machine prints
4–30–2020 15:35 to the console. What does the
M-dd-yyyy HH:mm indicate?
This isn’t enough. What do these mean?
Hasn’t some kind soul produced a fun table?
Well, that formatting is quite complicated. But formatting is only so good if you can create a
There are a whole host of Locales available on your machine. These are available by listing them using
NSLocale.isoCountryCodes or we can obtain the current Locale by using
If you want to use the correct, UK Locale you can or use the backwards American way by doing the following:
let usLocaleFormatter = DateFormatter()
usLocaleFormatter.dateStyle = .medium
usLocaleFormatter.locale = Locale(identifier: "en_US")
print (usLocaleFormatter.string(from: now))let ukLocaleFormatter = DateFormatter()
ukLocaleFormatter.dateStyle = .medium
ukLocaleFormatter.locale = Locale(identifier: "en_UK")
print (ukLocaleFormatter.string(from: now))
with the second obviously being the right way. Other
Locales can vary.
Create a Date from a Date component
One rather nice way of calculating
var components = DateComponents()
components.day = 30
components.month = 04
components.year = 2020
components.hour = 4
components.minute = 21
let newDate = Calendar.current.date(from: components) // OPTIONAL
This gives us a new date which outputs
2020–04–30 03:21:00 +0000 when printed, note the lack of formatting in this answer. Also the newDate here is an optional Date.
The other interesting component is the calendar. By using
Calendar.current we obtain the current calendar, but there are alternatives. We could use
autoupdatingCurrent which tracks changes to the user’s calendar.
But what about calendars that aren’t the user’s current version? No problem; here is a list of calendars in which you might be interested:
buddist Identifier for the Buddhist calendar.
chinese Identifier for the Chinese calendar.
coptic Identifier for the Coptic calendar.
ethiopicAmeteAlem Identifier for the Ethiopic (Amete Alem) calendar.
ethiopicAmeteMihret Identifier for the Ethiopic (Amete Mihret) calendar.
gregorian Identifier for the Gregorian calendar.
hebrew Identifier for the Hebrew calendar.
indian Identifier for the Indian calendar.
islamic Identifier for the Islamic calendar.
islamicCivil Identifier for the Islamic civil calendar.
islamicTabular Identifier for the tabular Islamic calendar.
islamicUmmAlQura Identifier for the Islamic Umm al-Qura calendar, as used in Saudi Arabia.
iso86010 Identifier for the ISO8601 calendar.
japanese Identifier for the Japanese calendar.
persian Identifier for the Persian calendar.
republicOfChina Identifier for the Republic of China (Taiwan) calendar.
Which can then be initialized in a Calendar using:
The calendar also has lots of great methods that can help you out in a pinch.
Want the timezone ?
Calendar.autoupdatingCurrent.timeZone will help us out. The time between two dates?
func compare(_ date1: Date, to date2: Date, toGranularity component: Calendar.Component) -> ComparisonResult will help you out. For the full list you can have a look at the documentation from Apple.
Date conforms to the comparable protocol, which means that <, > and = can be used to compare dates exactly how you would expect. That is, return a boolean when the expression is resolved.
newDate < now
> are handled in similar ways.
Date components from a Date
A date can be separated out into the components that make up that date. The list goes right down to the
nanosecond unit — so small!
let nowComponents = Calendar.autoupdatingCurrent.dateComponents([.year, .month, .day, .hour], from: now)nowComponents.year // The current year Int?
nowComponents.month // The current month Int?
nowComponents.day // The current day Int?
nowComponents.hour // The current hour Int?
These components are useful — more about them later!
Putting it all together: Days between two dates
Wouldn’t it be nice to have a function that calculates the days between dates. This uses a
dateformatter and two dates that are represented as Strings (which would only make sense if they are in the String format
yyyy-MM-dd of course).
This doesn’t really perform any validation (although if the date isn’t of the format
yyyy-MM-dd the function should return nil)
If we want to validate a String with the format
yyyy-MM-dd (in the current
Locale) we can make benefit from the
date initializer returning an optional date and ensure that a date is valid.
The date manager
To create a DateManager, we can set up a
DateFormatter as a lazy var and all will be rather splendid if we access the manager through a Singleton instance.
But before we go that far, let us look at…
These tests work in the Playground, providing you
import XCTest, and
Pardon the force-unwrapping, and the testing that is rather lacking (just one date per function?). Still…
we can then create a date manager that runs these tests sucessfully.
If you don’t like calling the Singleton directly and think it should be called from Date instead? That sounds like an extension to me!
Phew! That’s quite a lot of text for dates. It is interesting to take the journey from making simple
Strings, and then moving onto a singleton manager and all the way to an extension
It’s quite a lot — but I hope that this article has given you some way to understand this important set of functions in Swift.
Thanks for reading!
Extend your knowledge
- Apple have documentation around data and time
The Twitter contact:
Any questions? You can get in touch with me HERE