Làm thế nào để bạn tạo một đối tượng Ngày Swift?


156

Làm thế nào để bạn tạo một đối tượng ngày từ một ngày trong xcode nhanh chóng.

ví dụ: trong javascript bạn sẽ làm: var day = new Date('2014-05-20');


10
Sử dụng NSDategiống như trong Objective-C?
rmaddy

Câu trả lời:


262

Swift có Dateloại riêng của mình . Không cần sử dụng NSDate.

Tạo một ngày và thời gian trong Swift

Trong Swift, ngày và giờ được lưu trữ trong số dấu phẩy động 64 bit đo số giây kể từ ngày tham chiếu ngày 1 tháng 1 năm 2001 lúc 00:00:00 UTC . Điều này được thể hiện trong Datecấu trúc . Sau đây sẽ cung cấp cho bạn ngày và giờ hiện tại:

let currentDateTime = Date()

Để tạo thời gian ngày khác, bạn có thể sử dụng một trong các phương pháp sau.

Phương pháp 1

Nếu bạn biết số giây trước hoặc sau ngày tham chiếu năm 2001, bạn có thể sử dụng số đó.

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

Cách 2

Tất nhiên, sẽ dễ dàng hơn khi sử dụng những thứ như năm, tháng, ngày và giờ (thay vì giây tương đối) để tạo ra một Date. Đối với điều này, bạn có thể sử dụng DateComponentsđể chỉ định các thành phần và sau đó Calendarđể tạo ngày. Việc Calendarđưa ra Datebối cảnh. Nếu không, làm thế nào nó biết múi giờ hoặc lịch để thể hiện nó trong?

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

Viết tắt múi giờ khác có thể được tìm thấy ở đây . Nếu bạn để trống, thì mặc định là sử dụng múi giờ của người dùng.

Phương pháp 3

Cách ngắn gọn nhất (nhưng không nhất thiết là tốt nhất) có thể là sử dụng DateFormatter.

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

Các tiêu chuẩn kỹ thuật Unicode hiển thị các định dạng khácDateFormatterhỗ trợ.

Ghi chú

Xem câu trả lời đầy đủ của tôi để biết cách hiển thị ngày và giờ ở định dạng có thể đọc được. Cũng đọc những bài viết tuyệt vời này:


118

Điều này được thực hiện tốt nhất bằng cách sử dụng một phần mở rộng cho NSDatelớp hiện có .

Tiện ích mở rộng sau đây thêm trình khởi tạo mới sẽ tạo một ngày trong miền địa phương hiện tại bằng cách sử dụng chuỗi ngày theo định dạng bạn đã chỉ định.

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

Bây giờ bạn có thể tạo NSDate từ Swift chỉ bằng cách thực hiện:

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

Xin lưu ý rằng việc triển khai này không lưu trữ NSDateFormatter, điều mà bạn có thể muốn thực hiện vì lý do hiệu suất nếu bạn muốn tạo nhiều NSDates theo cách này.

Cũng xin lưu ý rằng việc triển khai này sẽ đơn giản gặp sự cố nếu bạn cố gắng khởi tạo một chuỗi NSDatebằng cách chuyển một chuỗi không thể được phân tích cú pháp chính xác. Điều này là do sự hủy bỏ bắt buộc của giá trị tùy chọn được trả về dateFromString. Nếu bạn muốn trả về một nilphân tích cú pháp xấu, lý tưởng nhất là bạn nên sử dụng trình khởi tạo không thành công; nhưng bạn không thể làm điều đó ngay bây giờ (tháng 6 năm 2015), do hạn chế trong Swift 1.2, do đó, lựa chọn tốt nhất tiếp theo của bạn là sử dụng phương thức xuất xưởng lớp.

Một ví dụ chi tiết hơn, giải quyết cả hai vấn đề, có tại đây: https://gist.github.com/algal/09b08515460b7bd229fa .


Cập nhật cho 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
Vậy từ "tiện lợi" làm gì ở đây?
James đốt cháy

18
Nó chỉ ra rằng tiện ích mở rộng đang cung cấp một trình khởi tạo mà trên thực tế ủy quyền khởi tạo cho trình khởi tạo được chỉ định đã tồn tại, trong trường hợp này là trình khởi tạo NSDate.init (timeInterval :, từDate :). Điều này được mô tả trong trang 275 của cuốn sách Ngôn ngữ lập trình Swift.
tảo

1
Một lỗ hổng tôi thấy trong đoạn mã trên là tạo lại NSDateFormattermỗi khi hàm được truy cập. Vì Hướng dẫn định dạng ngày tạo trạng thái Định dạng ngày không phải là thao tác rẻ . Ngoài ra, lớp này là luồng an toàn kể từ iOS7, vì vậy người ta có thể sử dụng nó một cách an toàn cho tất cả các NSDatetiện ích mở rộng.
Yevhen Dubinin

@eugenedubinin đúng. đó là lý do tại sao tôi nói "việc triển khai này không lưu trữ NSDateFormatter, mà bạn có thể muốn thực hiện vì lý do hiệu suất".
tảo

1
Hạn chế trong Swift 1.2 là gì? Tính năng "trình khởi tạo khả dụng" đã có sẵn kể từ Swift 1.1 .
Franklin Yu

48

Swift không có loại Ngày riêng, nhưng bạn sử dụng NSDateloại Cacao hiện có , ví dụ:

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

Mà bạn có thể sử dụng như:

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

Tôi đã phải thay đổi dòng: var date = gregorian? .DateFromComponents (c) nhưng tôi không hiểu tại sao và ngày trả lại phân tích cú phápFmt.dateFromString cũng phàn nàn. Bất kì manh mối nào? À, tôi đã thay đổi kiểu trả về của hàm từ NSDate thành NSDate? cho phép nó biên dịch. Nhưng tôi không chắc tại sao nó cần phải vô giá trị.
Thượng nghị sĩ

1
@TheSenator NSCalWiki init trả về một tùy chọn (?) Vì vậy gregorian là một tùy chọn (?). Truy cập một nhu cầu tùy chọn vào chuỗi tùy chọn để truy cập vào nó vì vậy gregorian? .DateFromCoomponents là chuỗi tùy chọn để mở khóa giá trị từ các tùy chọn gregorian (NSCalWiki). Thêm cho chuỗi tùy chọn ở đây
iluvatar_GR

NSGregorianCalWiki không được chấp nhận kể từ iOS 8, vì vậy hãy sử dụng NSCalWikiIdentifierGregorian thay thế
Hiệp sĩ Adam

2
@ Thượng nghị sĩ Khiếu nại vì Swift có cấu trúc ngày và mã này được phân phối lại Ngày là lớp. Vì vậy, bạn nên làm một cái gì đó như: 'Ngày gia hạn' và 'tĩnh func'
Zaporozhchenko Oleksandr

Một bản ghi nhớ về chuỗi phân tích trở lại Ngày. Một chuỗi "1979-07-01" với "yyyy-MM-dd" cho NSDateFormatterphương thức date(from:). Nó trở lại nil. Tôi tìm thấy vấn đề này trong nhật ký sự cố vải.io.
AechoLiu

12

Đây là cách tôi đã làm trong 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
    }
}

Sử dụng:

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

Mặc dù tôi đã vượt qua '15' như một ngày mà ngày trở lại mang đến ngày 14? Suy nghĩ?
Luke Zammit

@LukeZammit Thêm múi giờ vàodateComponents
Adrian

đã thử điều này không có may mắn - dateComponents.timeZone = TimeZone.c hiện tại, xin vui lòng?
Luke Zammit

5

Theo tài liệu của Apple

Thí dụ :

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

3
Đẹp nhưng không trả lời câu hỏi, câu hỏi không phải là khoảng thời gian.
zaph

4

Trong, Swift 3.0 bạn đã đặt đối tượng ngày theo cách này.

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 ở mọi nơi bạn muốn. Tạo tập tin riêng biệt, gọi nó là "Tiện ích mở rộng", idk.
Zaporozhchenko Oleksandr

2

Cá nhân tôi nghĩ rằng nó nên là một trình khởi tạo có sẵn:

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

Nếu không, một chuỗi với định dạng không hợp lệ sẽ đưa ra một ngoại lệ.


2

Theo câu trả lời của @mythz, tôi quyết định đăng phiên bản cập nhật của tiện ích mở rộng của mình bằng swift3cú pháp.

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

Tôi không sử dụng parsephương pháp, nhưng nếu ai đó cần, tôi sẽ cập nhật bài viết này.


1

Tôi thường có nhu cầu kết hợp các giá trị ngày từ một nơi với các giá trị thời gian cho một nơi khác. Tôi đã viết một chức năng trợ giúp để thực hiện điều này.

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

Theo Hướng dẫn định dạng dữ liệu của Apple

Tạo một định dạng ngày không phải là một hoạt động rẻ tiền. Nếu bạn có khả năng sử dụng một trình định dạng thường xuyên, thì việc lưu trữ một cá thể đơn lẻ sẽ hiệu quả hơn là tạo và loại bỏ nhiều phiên bản. Một cách tiếp cận là sử dụng biến tĩnh

Và mặc dù tôi đồng ý với @Leon rằng đây phải là trình khởi tạo khả dụng, khi bạn nhập dữ liệu hạt giống, chúng ta có thể có một dữ liệu không khả dụng (giống như có UIImage(imageLiteralResourceName:)).

Vì vậy, đây là cách tiếp cận của tôi:

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

Và bây giờ hãy tận hưởng cuộc gọi đơn giản:

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

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.