231

How do you create a date object from a date in swift xcode.

eg in javascript you would do: var day = new Date('2014-05-20');

craft
  • 2,017
  • 1
  • 21
  • 30
code_cookies
  • 3,910
  • 4
  • 16
  • 14

12 Answers12

374

Swift has its own Date type. No need to use NSDate.

Creating a Date and Time in Swift

In Swift, dates and times are stored in a 64-bit floating point number measuring the number of seconds since the reference date of January 1, 2001 at 00:00:00 UTC. This is expressed in the Date structure. The following would give you the current date and time:

let currentDateTime = Date()

For creating other date-times, you can use one of the following methods.

Method 1

If you know the number of seconds before or after the 2001 reference date, you can use that.

let someDateTime = Date(timeIntervalSinceReferenceDate: -123456789.0) // Feb 2, 1997, 10:26 AM

Method 2

Of course, it would be easier to use things like years, months, days and hours (rather than relative seconds) to make a Date. For this you can use DateComponents to specify the components and then Calendar to create the date. The Calendar gives the Date context. Otherwise, how would it know what time zone or calendar to express it in?

// Specify date components
var dateComponents = DateComponents()
dateComponents.year = 1980
dateComponents.month = 7
dateComponents.day = 11
dateComponents.timeZone = TimeZone(abbreviation: "JST") // Japan Standard Time
dateComponents.hour = 8
dateComponents.minute = 34

// Create date from components
let userCalendar = Calendar(identifier: .gregorian) // since the components above (like year 1980) are for Gregorian
let someDateTime = userCalendar.date(from: dateComponents)

Other time zone abbreviations can be found here. If you leave that blank, then the default is to use the user's time zone.

Method 3

The most succinct way (but not necessarily the best) could be to use DateFormatter.

let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm"
let someDateTime = formatter.date(from: "2016/10/08 22:31")

The Unicode technical standards show other formats that DateFormatter supports.

Notes

See my full answer for how to display the date and time in a readable format. Also read these excellent articles:

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
127

This is best done using an extension to the existing NSDate class.

The following extension adds a new initializer which will create a date in the current locale using the date string in the format you specified.

extension NSDate
{
    convenience
      init(dateString:String) {
      let dateStringFormatter = NSDateFormatter()
      dateStringFormatter.dateFormat = "yyyy-MM-dd"
      dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
      let d = dateStringFormatter.dateFromString(dateString)!
      self.init(timeInterval:0, sinceDate:d)
    }
 }

Now you can create an NSDate from Swift just by doing:

NSDate(dateString:"2014-06-06")

Please note that this implementation does not cache the NSDateFormatter, which you might want to do for performance reasons if you expect to be creating many NSDates in this way.

Please also note that this implementation will simply crash if you try to initialize an NSDate by passing in a string that cannot be parsed correctly. This is because of the forced unwrap of the optional value returned by dateFromString. If you wanted to return a nil on bad parses, you would ideally use a failible initializer; but you cannot do that now (June 2015), because of a limitation in Swift 1.2, so then you're next best choice is to use a class factory method.

A more elaborate example, which addresses both issues, is here: https://gist.github.com/algal/09b08515460b7bd229fa .


Update for Swift 5

extension Date {
    init(_ dateString:String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
        let date = dateStringFormatter.date(from: dateString)!
        self.init(timeInterval:0, since:date)
    }
}
LunaCodeGirl
  • 5,432
  • 6
  • 30
  • 36
algal
  • 27,584
  • 13
  • 78
  • 80
  • 10
    So what's the word "convenience" do here? – james Burns Jun 12 '14 at 15:47
  • 21
    It indicates that the extension is providing an initializer that in fact delegates initialization to an already-existing designated initializer, which in this case is the NSDate.init(timeInterval:,sinceDate:) initializer. This is described in page 275 of The Swift Programming Language book. – algal Jun 12 '14 at 21:08
  • 2
    One flaw I see in the code above is re-creating `NSDateFormatter` each time the function is accessed. As [Date Formatting Guide](https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/DataFormatting/Articles/dfDateFormatting10_4.html#//apple_ref/doc/uid/TP40002369-SW10) states _Creating a date formatter is not a cheap operation_. Also, the class is thread-safe since iOS7, so one can safely use it for all `NSDate` extensions. – Yevhen Dubinin Nov 22 '14 at 23:33
  • @eugenedubinin correct. that's why I said "this implementation does not cache the NSDateFormatter, which you might want to do for performance reasons". – algal Nov 23 '14 at 00:06
  • oops. sorry, missed that part. I've just got the version w/ cached `NSDateFormatter`. Would you like it to be posted as an edit or as a new answer? – Yevhen Dubinin Nov 23 '14 at 00:23
  • To cache the formatter you're going to need to store it somewhere, which means not using an extension or else relying on associated properties. Seems like that complicates things enough that it deserves to be its own answer. The simplicity of this one serves the simple case well. – algal Nov 23 '14 at 01:29
  • The problem I see with this is using a NSDate initializer that has a NSDate parameter. What if apple decides at some point to remove this parameter. Isn't there an init way to do this without relying on another init with a same-class parameter? – Mijail Feb 09 '16 at 10:37
  • @mijail That's longstanding, public API, which is central to the class's functionality. And in general, there's nothing suspect about initializers that have parameters of the type they're initializing. So I think it is quite unlikely it will be removed. So you could build this to depend on another initializer, but imho I don't think that would buy much benefit. – algal Feb 09 '16 at 20:42
  • This example is currently throwing Runtime Error – Alexandros Spyropoulos Mar 02 '16 at 00:48
  • @AlexandrosSpyropoulos I just pasted this code into a playground in Xcode 7.2.1 (7C1002) and it is not throwing a runtime error on my machine. – algal Mar 02 '16 at 00:53
  • Dah it apparently does if you try to extend the object and not just write an extension. – Alexandros Spyropoulos Mar 02 '16 at 01:01
  • Swift does not support extending objects, only extending types and protocols. So perhaps that explains it. – algal Mar 02 '16 at 04:05
  • 1
    What is the limitation in Swift 1.2? The feature "failable initializer" has been [available since Swift 1.1](https://developer.apple.com/swift/blog/?id=17). – Franklin Yu May 16 '16 at 21:44
  • @FranklinYu I think the compiler used to give an error saying that you could not call the superclass initializer until you had initialized all properties. – algal May 26 '16 at 21:52
  • @algal, how does that stop you from failable initializer? Just need to add a `return nil` before `super.init`. – Franklin Yu May 26 '16 at 22:19
  • This is an old post, but I disagree pretty strongly with the opening sentence that "This is best done using an extension to the existing NSDate class." @Suragch provides a couple of alternatives that seem much better: (DateComponents and a time interval since the reference date.) Plus, if you're gonna create `Date` Objects from strings of unknown format, you need error handling. Using a force-unwrap in this situation is bad practice, and promoting such code as *best* practice is irrisponsible, even if you point out that it will crash. – Duncan C Sep 04 '22 at 19:38
  • Finally, using an extension means you can't easily store a persistent `DateFormatter`, so the extension's function creates a new one each time. If this is used in a loop to process large numbers of dates it will be much slower than code that caches the DateFormatter. (Again, you mention the fact that your approach doesn't cache the DateFormatter, but using an extension makes it harder to cache it.) – Duncan C Sep 04 '22 at 19:40
53

Swift doesn't have its own Date type, but you to use the existing Cocoa NSDate type, e.g:

class Date {

    class func from(year: Int, month: Int, day: Int) -> Date {
        let gregorianCalendar = NSCalendar(calendarIdentifier: .gregorian)!

        var dateComponents = DateComponents()
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day

        let date = gregorianCalendar.date(from: dateComponents)!
        return date
    }

    class func parse(_ string: String, format: String = "yyyy-MM-dd") -> Date {
        let dateFormatter = DateFormatter()
        dateFormatter.timeZone = NSTimeZone.default
        dateFormatter.dateFormat = format

        let date = dateFormatter.date(from: string)!
        return date
    }
}

Which you can use like:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
mythz
  • 141,670
  • 29
  • 246
  • 390
  • I had to change the line: var date = gregorian?.dateFromComponents(c) but I don't understand why, and the parse return dateFmt.dateFromString is complaining too. Any clues? Ah, I have changed the return type of the function from NSDate to NSDate? which lets it compile. But I'm not sure why it needs to be nullable. – The Senator Nov 12 '14 at 21:46
  • 1
    @TheSenator NSCalendar init return an optional (?) so gregorian is an optional (?). Accessing an optional need to Optional chaining to accessing it so gregorian?.dateFromCoomponents is the Optional chaining to unwrap the value from the options gregorian (NSCalendar). More for Optional chaining [here](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/OptionalChaining.html) – iluvatar_GR Jan 04 '15 at 00:44
  • NSGregorianCalendar is deprecated as of iOS 8 so use NSCalendarIdentifierGregorian instead – Adam Knights Apr 14 '15 at 13:19
  • 2
    @The Senator Complains coz Swift has Date struct, and this code redeclare Date again as class. So u should do something like: 'extension Date' and 'static func' – Zaporozhchenko Oleksandr Feb 12 '18 at 22:44
  • A memo about parse string back to Date. A string "1979-07-01" with "yyyy-MM-dd" for `NSDateFormatter` method `date(from:)`. It returns `nil`. I found this issue in fabric.io crash log. – AechoLiu Oct 15 '18 at 06:45
23

Here's how I did it in Swift 4.2:

extension Date {

    /// Create a date from specified parameters
    ///
    /// - Parameters:
    ///   - year: The desired year
    ///   - month: The desired month
    ///   - day: The desired day
    /// - Returns: A `Date` object
    static func from(year: Int, month: Int, day: Int) -> Date? {
        let calendar = Calendar(identifier: .gregorian)
        var dateComponents = DateComponents()
        dateComponents.year = year
        dateComponents.month = month
        dateComponents.day = day
        return calendar.date(from: dateComponents) ?? nil
    }
}

Usage:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)
Adrian
  • 16,233
  • 18
  • 112
  • 180
7

According to Apple documentation

Example :

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Kevin Machado
  • 4,141
  • 3
  • 29
  • 54
6

According to @mythz answer, I decide to post updated version of his extension using swift3 syntax.

extension Date {
    static func from(_ year: Int, _ month: Int, _ day: Int) -> Date?
    {
        let gregorianCalendar = Calendar(identifier: .gregorian)
        let dateComponents = DateComponents(calendar: gregorianCalendar, year: year, month: month, day: day)
        return gregorianCalendar.date(from: dateComponents)
    }
}

I don't use parse method, but if someone needs, I will update this post.

hamsternik
  • 1,376
  • 3
  • 18
  • 26
5

In, Swift 3.0 you have set date object for this way.

extension Date
{
    init(dateString:String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        dateStringFormatter.locale = Locale(identifier: "en_US_POSIX")
        let d = dateStringFormatter.date(from: dateString)!
        self(timeInterval:0, since:d)
    }
}
Berlin
  • 2,115
  • 2
  • 16
  • 28
5

According to Apple's Data Formatting Guide

Creating a date formatter is not a cheap operation. If you are likely to use a formatter frequently, it is typically more efficient to cache a single instance than to create and dispose of multiple instances. One approach is to use a static variable

And while I agree with @Leon that this should preferably be a failable initializer, it doesn't always have to be (just like there is UIImage(imageLiteralResourceName:) that will crash if the resource doesn't exist).

So here's my approach:

extension DateFormatter {
  static let yyyyMMdd: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    formatter.calendar = Calendar(identifier: .iso8601)
    formatter.timeZone = TimeZone(secondsFromGMT: 0)
    formatter.locale = Locale(identifier: "en_US_POSIX")
    return formatter
  }()
}

extension Date {
    init?(yyyyMMdd: String) {
        guard let date = DateFormatter.yyyyMMdd.date(from: yyyyMMdd) else { return nil }
        self.init(timeInterval: 0, since: date)
    }

    init(dateLiteralString yyyyMMdd: String) {
        let date = DateFormatter.yyyyMMdd.date(from: yyyyMMdd)!
        self.init(timeInterval: 0, since: date)
    }
}

And now enjoy simply calling:

// For cases where the date exists for sure (eg. seed data)
Date(dateLiteralString: "2020-03-30")

// The rest of the time (eg. parsing unknown data)
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Arnaud
  • 17,268
  • 9
  • 65
  • 83
3

Personally I think it should be a failable initialiser:

extension Date {

    init?(dateString: String) {
        let dateStringFormatter = DateFormatter()
        dateStringFormatter.dateFormat = "yyyy-MM-dd"
        if let d = dateStringFormatter.date(from: dateString) {
            self.init(timeInterval: 0, since: d)
        } else {
            return nil
        }
    }
}

Otherwise a string with an invalid format will raise an exception.

Leon
  • 3,614
  • 1
  • 33
  • 46
2

I often have a need to combine date values from one place with time values for another. I wrote a helper function to accomplish this.

let startDateTimeComponents = NSDateComponents()
startDateTimeComponents.year = NSCalendar.currentCalendar().components(NSCalendarUnit.Year, fromDate: date).year
startDateTimeComponents.month = NSCalendar.currentCalendar().components(NSCalendarUnit.Month, fromDate: date).month
startDateTimeComponents.day = NSCalendar.currentCalendar().components(NSCalendarUnit.Day, fromDate: date).day
startDateTimeComponents.hour = NSCalendar.currentCalendar().components(NSCalendarUnit.Hour, fromDate: time).hour
startDateTimeComponents.minute = NSCalendar.currentCalendar().components(NSCalendarUnit.Minute, fromDate: time).minute
let startDateCalendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
combinedDateTime = startDateCalendar!.dateFromComponents(startDateTimeComponents)!
Justin Domnitz
  • 3,217
  • 27
  • 34
0

Since iOS15, Xcode 13 you can also create simple dates like this:

let date = try? Date("2022-02-14T20:15:00Z", strategy: .iso8601)
zero3nna
  • 2,770
  • 30
  • 28
0

You could create a custom initialization function like so:

extension Date {
    init(month: Int, day: Int, year: Int, hour: Int = 0, minute: Int = 0, second: Int = 0) {
        var dateComponents = DateComponents()
        dateComponents.month = month
        dateComponents.day = day
        dateComponents.year = year
        dateComponents.hour = hour
        dateComponents.minute = minute
        dateComponents.second = second
        dateComponents.timeZone = .current
        dateComponents.calendar = .current
        self = Calendar.current.date(from: dateComponents) ?? Date()
    }
}

Usage:

let date = Date(month: 5, day: 20, year: 2014)
Jacob G
  • 176
  • 1
  • 4