如何创建Swift Date对象?


156

如何从Swift XCode中的日期创建日期对象。

例如在JavaScript中,你会做: var day = new Date('2014-05-20');


10
是否NSDate像在Objective-C中一样使用?
rmaddy14年

Answers:


262

雨燕有自己的Date类型。无需使用NSDate

在Swift中创建日期和时间

在Swift中,日期和时间存储在一个64位浮点数中,该浮点数用于测量自2001年1月1日UTC的参考日期以来的秒数。这在Date结构中表示出来。以下内容将为您提供当前日期和时间:

let currentDateTime = Date()

若要创建其他日期时间,可以使用以下方法之一。

方法一

如果您知道2001年参考日期之前或之后的秒数,则可以使用该秒数。

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

方法二

当然,使用年,月,日和小时(而不是相对秒)之类的东西来制作会更容易Date。为此,您可以用于DateComponents指定组件,然后Calendar创建日期。在Calendar给出了Date上下文。否则,它将如何知道用哪个时区或日历表示?

// 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.current // user calendar
let someDateTime = userCalendar.date(from: dateComponents)

其他时区缩写可以在此处找到。如果您将其留空,则默认值为使用用户的时区。

方法3

最简洁的方式(但不一定是最好的方式)可以使用DateFormatter

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

统一的技术标准表明其他格式DateFormatter支持的。

笔记

请参阅我的完整答案,以了解如何以可读格式显示日期和时间。另请阅读以下优秀文章:


118

最好使用现有NSDate类的扩展来完成。

以下扩展添加了一个新的初始化程序,它将使用您指定的格式的日期字符串在当前语言环境中创建一个日期。

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)
    }
 }

现在,您只需执行以下操作即可从Swift创建NSDate:

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

请注意,此实现不会缓存NSDateFormatter,如果您希望以此方式创建许多NSDates,则出于性能原因可能要这样做。

另请注意,如果您尝试NSDate通过传入无法正确解析的字符串来初始化,则该实现将完全崩溃。这是因为强制解开了由返回的可选值dateFromString。如果您想返回nil错误的解析器,则理想情况下,您将使用故障初始化器。但由于Swift 1.2的限制,您现在不能执行此操作(2015年6月),因此,下一个最佳选择是使用类工厂方法。

一个更精致的示例可以解决这两个问题,位于:https : //gist.github.com/algal/09b08515460b7bd229fa


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)
    }
}

9
那么这里的“便利”一词是什么?
james Burns 2014年

18
它指示扩展名提供了一个初始化程序,该初始化程序实际上将初始化委托给一个已经存在的指定初始化程序,在这种情况下为NSDate.init(timeInterval:,sinceDate :)初始化程序。《 Swift编程语言》一书的第275页对此进行了描述。

1
我在上面的代码中看到的一个缺陷是NSDateFormatter每次访问该函数时都会重新创建。正如日期格式指南所指出的,创建日期格式器并不便宜。另外,该类自iOS7起就是线程安全的,因此可以安全地将其用于所有NSDate扩展。
Yevhen Dubinin

@eugenedubinin正确。这就是为什么我说“此实现不缓存NSDateFormatter,出于性能原因可能要这样做”。
algal

1
Swift 1.2的局限性是什么?自Swift 1.1以来,功能“失败的初始化器”已可用
富兰克林·于

48

Swift没有自己的Date类型,但是您可以使用现有的Cocoa NSDate类型,例如:

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
    }
}

您可以像这样使用:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)

我不得不更改这一行:var date = gregorian?.dateFromComponents(c),但我不明白为什么,而且解析返回dateFmt.dateFromString也在抱怨。有什么线索吗?嗯,我将函数的返回类型从NSDate更改为NSDate?它可以编译。但是我不确定为什么它需要为空。
参议员

1
@TheSenator NSCalendar init返回一个可选的(?),因此gregorian是一个可选的(?)。访问可选链接需要访问可选链接,以便使用gregorian?.dateFromCoomponents是从选项gregorian(NSCalendar)解开值的可选链接。更多有关可选链接的信息
iluvatar_GR,2015年

从iOS 8开始不推荐使用NSGregorianCalendar,因此请改用NSCalendarIdentifierGregorian
Adam Knights

2
@The Senator Compzins coz Swift具有Date结构,此代码再次将Date声明为类。因此,您应该执行以下操作:“扩展日期”和“静态功能”
Zaporozhchenko Oleksandr,

有关将字符串解析回Date的备忘录。的字符串“1979年7月1日”与“YYYY-MM-DD”为NSDateFormatter方法date(from:)。它返回nil。我在fabric.io崩溃日志中发现了此问题。
AechoLiu

12

这是我在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
    }
}

用法:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)

虽然我一天传递的是“ 15”,但返回的日期却是第14位?有什么想法吗?
卢克·扎米特

@LukeZammit添加时区至dateComponents
Adrian

尝试没有运气-dateComponents.timeZone = TimeZone.current,请问有什么想法?
Luke Zammit

5

根据Apple文档

范例:

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow

3
很好,但没有回答问题,问题不是时间间隔。
zaph 2014年

4

在Swift 3.0中,您已经通过这种方式设置了date对象。

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)
    }
}

@ Zonker.in.Geneva您想要的任何地方。创建单独的文件,将其称为“扩展名”,idk。
Zaporozhchenko Oleksandr,

2

我个人认为这应该是一个失败的初始化器:

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
        }
    }
}

否则,格式无效的字符串将引发异常。


2

根据@mythz的回答,我决定使用swift3语法发布他的扩展程序的更新版本。

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)
    }
}

我不使用parse方法,但是如果有人需要,我将更新此帖子。


1

我经常需要将一个地方的日期值与另一个地方的时间值结合起来。我编写了一个辅助函数来完成此任务。

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)!

0

根据苹果公司的数据格式指南

创建日期格式器并不是一项便宜的操作。如果您可能经常使用格式化程序,则缓存单个实例通常比创建和处置多个实例更为有效。一种方法是使用静态变量

尽管我同意@Leon的观点,它应该是可失败的初始化程序,但是当您输入种子数据时,我们可能会有一个无法失败的初始化程序(就像UIImage(imageLiteralResourceName:))。

所以这是我的方法:

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)
    }
}

现在享受简单的调用:

// For seed data
Date(dateLiteralString: "2020-03-30")

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.