Bagaimana Anda membuat objek Tanggal Swift?


156

Bagaimana Anda membuat objek tanggal dari tanggal di xcode cepat.

misalnya dalam javascript yang akan Anda lakukan: var day = new Date('2014-05-20');


10
Gunakan NSDateseperti di Objective-C?
rmaddy

Jawaban:


263

Swift memiliki Datetipe sendiri . Tidak perlu digunakan NSDate.

Membuat Tanggal dan Waktu dalam Swift

Di Swift, tanggal dan waktu disimpan dalam angka floating point 64-bit yang mengukur jumlah detik sejak tanggal referensi 1 Januari 2001 pukul 00:00:00 UTC . Ini diungkapkan dalam Datestruktur . Berikut ini akan memberi Anda tanggal dan waktu saat ini:

let currentDateTime = Date()

Untuk membuat tanggal-waktu lainnya, Anda dapat menggunakan salah satu metode berikut.

Metode 1

Jika Anda tahu jumlah detik sebelum atau setelah tanggal referensi 2001, Anda dapat menggunakannya.

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

Metode 2

Tentu saja, akan lebih mudah untuk menggunakan hal-hal seperti tahun, bulan, hari dan jam (daripada detik relatif) untuk membuat Date. Untuk ini, Anda dapat menggunakan DateComponentsuntuk menentukan komponen dan kemudian Calendaruntuk membuat tanggal. The Calendarmemberikan Datekonteks. Kalau tidak, bagaimana ia bisa tahu zona waktu atau kalender mana untuk mengekspresikannya?

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

Singkatan zona waktu lainnya dapat ditemukan di sini . Jika Anda membiarkannya kosong, maka defaultnya adalah menggunakan zona waktu pengguna.

Metode 3

Cara yang paling ringkas (tapi belum tentu yang terbaik) bisa digunakan DateFormatter.

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

Standar teknis Unicode menunjukkan format lain yang DateFormattermendukung.

Catatan

Lihat jawaban lengkap saya untuk cara menampilkan tanggal dan waktu dalam format yang dapat dibaca. Baca juga artikel-artikel hebat ini:


118

Ini paling baik dilakukan dengan menggunakan ekstensi ke NSDatekelas yang ada .

Ekstensi berikut menambahkan inisialisasi baru yang akan membuat tanggal di lokal saat ini menggunakan string tanggal dalam format yang Anda tentukan.

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

Sekarang Anda dapat membuat NSDate dari Swift hanya dengan melakukan:

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

Harap dicatat bahwa implementasi ini tidak menembolok NSDateFormatter, yang mungkin ingin Anda lakukan karena alasan kinerja jika Anda berharap akan membuat banyak NSDatedengan cara ini.

Harap perhatikan juga bahwa implementasi ini hanya akan lumpuh jika Anda mencoba menginisialisasi NSDatedengan meneruskan string yang tidak dapat diuraikan dengan benar. Ini karena pembongkaran paksa nilai opsional yang dikembalikan oleh dateFromString. Jika Anda ingin mengembalikan nilparse buruk, Anda idealnya menggunakan inisialisasi yang gagal; tetapi Anda tidak dapat melakukannya sekarang (Juni 2015), karena keterbatasan dalam Swift 1.2, maka Anda pilihan terbaik berikutnya adalah menggunakan metode pabrik kelas.

Contoh yang lebih terperinci, yang membahas kedua masalah, ada di sini: https://gist.github.com/algal/09b08515460b7bd229fa .


Pembaruan untuk 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
Jadi apa kata "kenyamanan" lakukan di sini?
james Burns

18
Ini menunjukkan bahwa ekstensi menyediakan inisialisasi yang sebenarnya mendelegasikan inisialisasi ke inisialisasi yang sudah ada, yang dalam hal ini adalah NSDate.init (timeInterval:, sinceDate :) initializer. Ini dijelaskan di halaman 275 dari buku Bahasa Swift Programming.
algal

1
Satu kekurangan yang saya lihat dalam kode di atas adalah menciptakan kembali NSDateFormattersetiap kali fungsi diakses. Seperti yang dinyatakan dalam Panduan Pemformatan Tanggal Membuat formatter tanggal bukanlah operasi yang murah . Juga, kelas ini aman karena iOS7, sehingga orang dapat menggunakannya dengan aman untuk semua NSDateekstensi.
Yevhen Dubinin

@ eugenedubinin benar. itu sebabnya saya berkata "implementasi ini tidak men-cache NSDateFormatter, yang mungkin ingin Anda lakukan karena alasan kinerja".
algal

1
Apa batasan dalam Swift 1.2? Fitur "inisialisasi failable" telah tersedia sejak Swift 1.1 .
Franklin Yu

48

Swift tidak memiliki jenis Tanggal sendiri, tetapi Anda harus menggunakan NSDatejenis Kakao yang ada , misalnya:

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

Yang bisa Anda gunakan seperti:

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

Saya harus mengubah baris: var date = gregorian? .DateFromComponents (c) tapi saya tidak mengerti mengapa, dan parse return dateFmt.dateFromString juga mengeluh. Ada petunjuk? Ah, saya telah mengubah jenis fungsi kembali dari NSDate ke NSDate? yang memungkinkannya dikompilasi. Tapi saya tidak yakin mengapa itu harus dibatalkan.
Senator

1
@TheSenator NSCalendar init mengembalikan opsional (?) Jadi gregorian adalah opsional (?). Mengakses kebutuhan opsional ke Chaining opsional untuk mengaksesnya dengan begitu asli? .DateFromCoomponents adalah Chaining opsional untuk membuka nilai dari opsi gregorian (NSCalendar). Lebih banyak untuk
rangkaian

NSGregorianCalendar sudah tidak digunakan lagi di iOS 8 jadi gunakan NSCalendarIdentifierGregorian sebagai gantinya
Adam Knights

2
@The Senator Complains karena Swift memiliki struct Tanggal, dan kode ini mendeklarasikan Tanggal kembali sebagai kelas. Jadi Anda harus melakukan sesuatu seperti: 'extension Date' dan 'static func'
Zaporozhchenko Oleksandr

Memo tentang string parse kembali ke Date. Sebuah string "1979-07-01" "dengan" yyyy-MM-dd "untuk NSDateFormattermetode date(from:). Ia kembali nil. Saya menemukan masalah ini di log crash fabric.io.
AechoLiu

12

Inilah cara saya melakukannya di 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
    }
}

Pemakaian:

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

Meskipun saya melewati '15' sebagai hari, tanggal yang dikembalikan memberikan tanggal 14? Pikiran?
Luke Zammit

@LukeZammit Tambahkan zona waktu kedateComponents
Adrian

mencoba ini tanpa hasil - dateComponents.timeZone = TimeZone.current, tolong pikirkan?
Luke Zammit

5

Berdasarkan dokumentasi Apple

Contoh:

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

3
Bagus tapi tidak menjawab pertanyaan, pertanyaannya bukan tentang interval waktu.
zaf

4

Di, Swift 3.0 Anda telah menetapkan objek tanggal untuk cara ini.

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 di mana-mana yang Anda inginkan. Buat file terpisah, sebut saja "Extension", idk.
Zaporozhchenko Oleksandr

2

Secara pribadi saya pikir itu harus menjadi inisialisasi yang tersedia:

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

Kalau tidak, string dengan format yang tidak valid akan memunculkan eksepsi.


2

Menurut jawaban @mythz, saya memutuskan untuk mengirim versi terbaru ekstensi menggunakan swift3sintaks.

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

Saya tidak menggunakan parsemetode, tetapi jika seseorang membutuhkan, saya akan memperbarui posting ini.


1

Saya sering harus menggabungkan nilai tanggal dari satu tempat dengan nilai waktu untuk yang lain. Saya menulis fungsi pembantu untuk mencapai ini.

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

Menurut Panduan Pemformatan Data Apple

Membuat formatter tanggal bukanlah operasi yang murah. Jika Anda cenderung sering menggunakan formatter, biasanya lebih efisien untuk melakukan cache satu instance daripada membuat dan membuang beberapa instance. Salah satu pendekatan adalah menggunakan variabel statis

Dan sementara saya setuju dengan @Leon bahwa ini harus menjadi inisialisasi failable, ketika Anda memasukkan data seed, kita bisa memiliki satu yang tidak tersedia (seperti ada UIImage(imageLiteralResourceName:) ).

Jadi, inilah pendekatan saya:

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

Dan sekarang nikmati hanya menelepon:

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

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.