Wie erstellt man ein Swift Date-Objekt?

156

Wie erstelle ich ein Datumsobjekt aus einem Datum in Swift Xcode?

zB in Javascript würden Sie tun: var day = new Date('2014-05-20');

code_cookies
quelle
10
Verwenden Sie NSDategenau wie in Objective-C?
rmaddy

Antworten:

262

Swift hat seinen eigenen DateTyp. Keine Notwendigkeit zu verwenden NSDate.

Erstellen eines Datums und einer Uhrzeit in Swift

In Swift werden Datum und Uhrzeit in einer 64-Bit-Gleitkommazahl gespeichert, die die Anzahl der Sekunden seit dem Referenzdatum vom 1. Januar 2001 um 00:00:00 UTC misst . Dies drückt sich in der DateStruktur aus . Folgendes gibt Ihnen das aktuelle Datum und die aktuelle Uhrzeit an:

let currentDateTime = Date()

Zum Erstellen anderer Datums- und Uhrzeitangaben können Sie eine der folgenden Methoden verwenden.

Methode 1

Wenn Sie die Anzahl der Sekunden vor oder nach dem Referenzdatum 2001 kennen, können Sie diese verwenden.

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

Methode 2

Natürlich wäre es einfacher, Dinge wie Jahre, Monate, Tage und Stunden (anstatt relative Sekunden) zu verwenden, um eine zu erstellen Date. Hierzu können Sie DateComponentsdie Komponenten angeben und anschließend Calendardas Datum erstellen. Das Calendargibt den DateKontext. Wie würde es sonst wissen, in welcher Zeitzone oder in welchem ​​Kalender es ausgedrückt werden soll?

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

Weitere Zeitzonenabkürzungen finden Sie hier . Wenn Sie dieses Feld leer lassen, wird standardmäßig die Zeitzone des Benutzers verwendet.

Methode 3

Der prägnanteste Weg (aber nicht unbedingt der beste) könnte sein DateFormatter.

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

Die Unicode - technischen Standards zeigen andere Formate , dass DateFormatterStützen.

Anmerkungen

In meiner vollständigen Antwort erfahren Sie, wie Sie Datum und Uhrzeit in einem lesbaren Format anzeigen. Lesen Sie auch diese ausgezeichneten Artikel:

Suragch
quelle
118

Dies geschieht am besten mit einer Erweiterung der vorhandenen NSDateKlasse.

Die folgende Erweiterung fügt einen neuen Initialisierer hinzu, der ein Datum im aktuellen Gebietsschema unter Verwendung der Datumszeichenfolge in dem von Ihnen angegebenen Format erstellt.

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

Jetzt können Sie aus Swift ein NSDate erstellen, indem Sie Folgendes tun:

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

Beachten Sie, dass diese Implementierung den NSDateFormatter nicht zwischenspeichert, was Sie möglicherweise aus Leistungsgründen tun möchten, wenn Sie erwarten, dass viele NSDates auf diese Weise erstellt werden.

Bitte beachten Sie auch, dass diese Implementierung einfach abstürzt, wenn Sie versuchen, eine zu initialisieren, NSDateindem Sie eine Zeichenfolge übergeben, die nicht korrekt analysiert werden kann. Dies liegt am erzwungenen Entpacken des von zurückgegebenen optionalen Werts dateFromString. Wenn Sie eine nilfehlerhafte Analyse zurückgeben möchten, verwenden Sie idealerweise einen fehlerhaften Initialisierer. Aufgrund einer Einschränkung in Swift 1.2 können Sie dies jetzt (Juni 2015) nicht tun. Daher ist die nächstbeste Wahl die Verwendung einer Klassenfactory-Methode.

Ein ausführlicheres Beispiel, das beide Probleme behandelt, finden Sie hier: https://gist.github.com/algal/09b08515460b7bd229fa .


Update für 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)
    }
}
Algen
quelle
9
Was macht das Wort "Bequemlichkeit" hier?
James Burns
18
Es zeigt an, dass die Erweiterung einen Initialisierer bereitstellt, der die Initialisierung tatsächlich an einen bereits vorhandenen festgelegten Initialisierer delegiert, in diesem Fall an den Initialisierer NSDate.init (timeInterval :, SinceDate :). Dies wird auf Seite 295 des Buches The Swift Programming Language beschrieben.
Algen
1
Ein Fehler, den ich im obigen Code sehe, ist die Neuerstellung bei NSDateFormatterjedem Zugriff auf die Funktion. Wie Datumsformatierung Handbuch besagt ein Datum Erstellen Formatter kein billiges Betrieb ist . Außerdem ist die Klasse seit iOS7 threadsicher, sodass sie sicher für alle NSDateErweiterungen verwendet werden kann.
Yevhen Dubinin
@ Eugenedubinin richtig. Aus diesem Grund habe ich gesagt, dass "diese Implementierung den NSDateFormatter nicht zwischenspeichert, was Sie möglicherweise aus Leistungsgründen tun möchten".
Algen
1
Was ist die Einschränkung in Swift 1.2? Die Funktion "Failable Initialisierer" ist seit Swift 1.1 verfügbar .
Franklin Yu
48

Swift hat keinen eigenen Datumstyp, aber Sie können den vorhandenen Kakaotyp NSDateverwenden, z.

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

Welche Sie verwenden können wie:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
Mythos
quelle
Ich musste die Zeile ändern: var date = gregorian? .DateFromComponents (c), aber ich verstehe nicht warum, und die Analyse gibt dateFmt.dateFromString zurück. Irgendwelche Hinweise? Ah, ich habe den Rückgabetyp der Funktion von NSDate in NSDate geändert. das lässt es kompilieren. Aber ich bin mir nicht sicher, warum es nullbar sein muss.
Der Senator
1
@TheSenator NSCalendar init gibt ein optionales (?) Zurück, sodass Gregorian ein optionales (?) Ist. Zugriff auf eine optionale Notwendigkeit zur optionalen Verkettung, um darauf zuzugreifen, also gregorian? .DateFromCoomponents ist die optionale Verkettung, um den Wert aus den Optionen gregorian (NSCalendar) zu entpacken. Mehr für optionale Verkettung hier
iluvatar_GR
NSGregorianCalendar ist ab iOS 8 veraltet. Verwenden Sie stattdessen NSCalendarIdentifierGregorian
Adam Knights
2
@ Der Senator beschwert sich, weil Swift die Datumsstruktur hat und dieser Code das Datum erneut als Klasse deklariert. Also solltest du etwas tun wie: 'Verlängerungsdatum' und 'statische Funktion'
Zaporozhchenko Oleksandr
Ein Memo über die Analysezeichenfolge zurück zu Datum. Eine Zeichenfolge "1979-07-01" mit "JJJJ-MM-TT" für die NSDateFormatterMethode date(from:). Es kehrt zurück nil. Ich habe dieses Problem im Absturzprotokoll von Fabric.io gefunden.
AechoLiu
12

So habe ich es in Swift 4.2 gemacht:

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

Verwendung:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)
Adrian
quelle
Obwohl ich als Tag '15' übergebe, gibt das zurückgegebene Datum den 14.? Gedanken?
Luke Zammit
@ LukeZammit Fügen Sie eine Zeitzone zudateComponents
Adrian
versuchte dies ohne Glück - dateComponents.timeZone = TimeZone.current, Gedanken bitte?
Luke Zammit
5

Laut Apple-Dokumentation

Beispiel:

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Kevin Machado
quelle
3
Schön, beantwortet aber die Frage nicht, es geht nicht um Zeitintervalle.
Zaph
4

In Swift 3.0 haben Sie auf diese Weise ein Datumsobjekt festgelegt.

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)
    }
}
Jaydip
quelle
@ Zonker.in.Geneva wo immer du willst. Erstellen Sie eine separate Datei, nennen Sie sie "Erweiterung", idk.
Zaporozhchenko Oleksandr
2

Persönlich denke ich, dass es ein fehlerhafter Initialisierer sein sollte:

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

Andernfalls löst eine Zeichenfolge mit einem ungültigen Format eine Ausnahme aus.

Leon
quelle
2

Laut @mythz Antwort entscheide ich mich, eine aktualisierte Version seiner Erweiterung mit swift3Syntax zu veröffentlichen.

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

Ich benutze keine parseMethode, aber wenn jemand benötigt, werde ich diesen Beitrag aktualisieren.

Hamsternik
quelle
1

Ich muss oft Datumswerte von einem Ort mit Zeitwerten für einen anderen kombinieren. Ich habe eine Hilfsfunktion geschrieben, um dies zu erreichen.

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
quelle
0

Laut Apples Datenformatierungshandbuch

Das Erstellen eines Datumsformatierers ist keine billige Operation. Wenn Sie wahrscheinlich häufig einen Formatierer verwenden, ist es in der Regel effizienter, eine einzelne Instanz zwischenzuspeichern, als mehrere Instanzen zu erstellen und zu entsorgen. Ein Ansatz besteht darin, eine statische Variable zu verwenden

Und obwohl ich @Leon zustimme, dass dies ein fehlgeschlagener Initialisierer sein sollte, könnten wir bei der Eingabe von Startdaten einen haben, der nicht fehlgeschlagen werden kann (genau wie es ihn gibt UIImage(imageLiteralResourceName:)).

Also hier ist mein Ansatz:

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

Und jetzt viel Spaß beim Anrufen:

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

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Nycen
quelle