An NSDate object specifies a particular point in time. Despite “date” being in its name, it really stores time, of which the date is a component. Under the hood, NSDates (as far as the public superclass we can see is concerned) are specified as a number of seconds past a reference date. NSDate’s reference date is the first moment of January 1, 2001 in the GMT timezone. These seconds are stored as a Double, which allows NSDate to range from milliseconds to years. What we see as an NSDate is actually an abstract public superclass for a cluster of classes related to dates. The internal classes are private, so talking about them here wouldn’t be particularly helpful, and it is probably best not to mess with them anyway.
Since Swift 3 though, most “NS” prefixes were removed from the Swift Standard Library, so now it is simply called “Date”. If you need to save a timestamp for something in your iOS app, or maybe have a countdown timer for an app or game, you will be dealing with the Date class, so let’s take a look at it.
Creating an Date Object
The Date class itself has 6 initializers: 3 dedicated and 3 convenience ones. One dedicated initializer deals with Coding and 4 of them deal with TimeInterval. I personally only use one of the built in initializers to Date, and that is the one that takes no arguments. This one creates n Date object specifying the exact moment it was called. It is incredibly simple:
//Prototype: init() let now = Date()
That is all there is to it, now the “now” variable stores the exact moment this initializer was called. I’ve used this in a few examples before to generate a simple class to test with.
I originally wrote this going through the other initializers that deal with TimeInterval, but decided to not include them. TimeInterval is a typealias for a Double, and it specifies a certain number of seconds. It has initializers for an TimeInterval since the reference date, since now, since the first moment of January 1, 1970 (the Unix epoch), and since any other Date object.
I don’t know about you, but I can’t really read a few million seconds and see that it is referring to this year. Instead, if I need to specify a particular Date, I usually use DateComponents. We will cover it a bit more in depth later, but for the moment, I will show a simple implementation of using this to create a certain Date:
let todayComponents = DateComponents(year: 2014, month: 11, day: 12) if let todayDate = Calendar.current.date(from: todayComponents) { print(todayDate) } //Output: "2014-11-12 08:00:00 +0000"
The first part is pretty simple. You make a DateComponents object, and then set the components you wish.
An DateComponents object lets you set the components, but it has very little meaning without the context given by a Calendar. Different calendrical systems have different numbers of seconds for some of these components, particularly for the length of a year. So, to create a Date, we call that dateFromComponents method of the Calendar class and pass in the todayComponents object we just made. The actual object we are calling that from is is the return of Calendar’s currentCalendar class function. This returns an object specifying the current calendar that the device is set to. You can specify one if you wish, but it is usually easier (and in many situations better) to use the current one for that device.
The dateFromComponents method returns an optional Date, so we can use optional binding to make it into a standard Date. It could return nil if it is unable to convert the components to a specific time for some reason. If you set a component out of its normal bounds, it will usually roll over. For instance, if you set the minutes component to 1, and the seconds component to 121 seconds, this will return something with the will be interpreted as 2 minutes and one second, and just add to the already specified minute value, so resulting in 3 minutes and 1 second. I am not sure when it is unable to actually generate an Date from components, but I know very little about other Calendars, so maybe it has more to do with the conversions or idiosyncrasies between how other calendars handle the components compared to the Gregorian calendar. Nonetheless, this can return nil, so I would suggest either checking it against nil before force unwrapping, or just using Swift’s Optional Binding syntax when using this for your iOS apps.
Also, apparently this calculation uses the time zone for your computer, so that is the reason it returned 8 AM in the GMT timezone. This computer’s time zone is the Pacific Timezone, which is -8 GMT, as such midnight here (as specified by not setting the hour, minute, second, etc components, corresponds to 8 AM GMT. If we had also set the timezone explicitly as GMT in our DateComponents object, it would return a value with 0 hours with 0 hours offset.
Comparing Dates
Date comes with 4 instance methods to compare dates. The first we will talk about is the most general, the compare method. This method returns an ComparisonResult, which is an enumeration that says whether a date is before, same as, or after another date. I’ve personally found ComparisonResult difficult to remember which way means what, but Apple provided a nice explanation, which I will paraphrase in tabular form below:
ComparisonResult | Meaning |
orderedAscending | Instance Date is earlier than anotherDate. |
orderedSame | Instance Date and anotherDate are equal. |
orderedDescending | Instance date is later than anotherDate. |
Here is some code to test it in a Swift Playground:
let result = now.compare(hourFromNow) if result == .orderedAscending { print("Earlier") } else if result == .orderedDescending { print("Later") } //Outputs: "Earlier"
Conclusion
The Date abstract class itself is a pretty simple interface. It has some initializers, and basic comparisons between different Date classes, and that’s about it.
If you really want to get a lot of use out of it, you will need to work with a few different classes like DateComponents, Calendar, and DateFormatter. I have covered DateFormatter a bit in Objective-C, but there will probably be a Swift version soon enough. We’ll cover DateComponents a bit more in depth later, but I felt it was better to give some information about it now because it is a whole lot easier to create an Date object with DateFormatter than with an TimeInterval (at least if you have to work with more than a day’s worth of time anyway).
I hope you found this article helpful. If you did, please don’t hesitate to share this post on Twitter or your social media of choice, every share helps. Of course, if you have any questions, don’t hesitate to contact me on the Contact Page, or on Twitter @CodingExplorer, and I’ll see what I can do. Thanks!