Come si crea un oggetto Swift Date?


157

Come si crea un oggetto data da una data in xcode rapido.

ad esempio in JavaScript devi fare: var day = new Date('2014-05-20');


10
Usa NSDateproprio come in Objective-C?
rmaddy,

Risposte:


263

Swift ha il suo Datetipo. Non c'è bisogno di usare NSDate.

Creazione di una data e ora in Swift

In Swift, le date e gli orari sono memorizzati in un numero in virgola mobile a 64 bit che misura il numero di secondi dalla data di riferimento del 1 gennaio 2001 alle 00:00:00 UTC . Questo è espresso nella Datestruttura . Quanto segue ti darebbe la data e l'ora correnti:

let currentDateTime = Date()

Per creare altre date-ora, è possibile utilizzare uno dei seguenti metodi.

Metodo 1

Se conosci il numero di secondi prima o dopo la data di riferimento del 2001, puoi usarlo.

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

Metodo 2

Certo, sarebbe più facile usare cose come anni, mesi, giorni e ore (piuttosto che secondi relativi) per fare un Date. Per questo è possibile utilizzare DateComponentsper specificare i componenti e quindi Calendarper creare la data. La Calendardà il Datecontesto. Altrimenti, come potrebbe sapere in quale fuso orario o calendario esprimerlo?

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

Altre abbreviazioni del fuso orario sono disponibili qui . Se lo lasci vuoto, l'impostazione predefinita è utilizzare il fuso orario dell'utente.

Metodo 3

Il modo più conciso (ma non necessariamente il migliore) potrebbe essere l'uso DateFormatter.

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

Gli standard tecnici Unicode mostrano altri formati che DateFormattersupportano.

Appunti

Vedi la mia risposta completa per come visualizzare la data e l'ora in un formato leggibile. Leggi anche questi eccellenti articoli:


118

Questo è fatto meglio usando un'estensione alla NSDateclasse esistente .

La seguente estensione aggiunge un nuovo inizializzatore che creerà una data nella locale corrente usando la stringa della data nel formato specificato.

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

Ora puoi creare un NSDate da Swift semplicemente facendo:

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

Questa implementazione non memorizza nella cache NSDateFormatter, cosa che potresti voler fare per motivi di prestazioni se ti aspetti di creare molti messaggi NSDatein questo modo.

Si noti inoltre che questa implementazione si arresta in modo anomalo se si tenta di inizializzare un NSDatepassaggio in una stringa che non può essere analizzata correttamente. Ciò è dovuto allo spostamento forzato del valore facoltativo restituito da dateFromString. Se si desidera restituire un nilerrore in analisi, si dovrebbe idealmente utilizzare un inizializzatore non riuscito; ma non puoi farlo ora (giugno 2015), a causa di una limitazione in Swift 1.2, quindi la scelta migliore è quella di utilizzare un metodo factory di classe.

Un esempio più elaborato, che affronta entrambi i problemi, è qui: https://gist.github.com/algal/09b08515460b7bd229fa .


Aggiornamento per 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
Allora, qual è la parola "convenienza" fare qui?
James Burns,

18
Indica che l'estensione fornisce un inizializzatore che in effetti delega l'inizializzazione a un inizializzatore designato già esistente, che in questo caso è l'inizializzatore NSDate.init (timeInterval:, sinceDate :). Questo è descritto a pagina 275 del libro The Swift Programming Language.
Algal,

1
Un difetto che vedo nel codice sopra sta ricreando NSDateFormatterogni volta che si accede alla funzione. Come afferma la Guida alla formattazione della data , la creazione di un formatter data non è un'operazione economica . Inoltre, la classe è thread-safe da iOS7, quindi si può tranquillamente usarla per tutte le NSDateestensioni.
Yevhen Dubinin,

@eugenedubinin correct. ecco perché ho detto "questa implementazione non memorizza nella cache NSDateFormatter, cosa che potresti voler fare per motivi di prestazioni".
Algal,

1
Qual è la limitazione in Swift 1.2? La funzione "inizializzatore disponibile" è disponibile da Swift 1.1 .
Franklin Yu,

48

Swift non ha il proprio tipo di data, ma è necessario utilizzare il NSDatetipo di cacao esistente , ad esempio:

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

Che puoi usare come:

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

Ho dovuto cambiare la riga: var date = gregorian? .DateFromComponents (c) ma non capisco perché, e anche la data di ritorno dell'analisiFmt.dateFromString si lamenta. Qualche indizio? Ah, ho cambiato il tipo di ritorno della funzione da NSDate a NSDate? che consente di compilare. Ma non sono sicuro del motivo per cui deve essere nullable.
Il senatore il

1
@TheSenator NSCalendar init restituisce un facoltativo (?) In modo che gregorian sia un facoltativo (?). Accedere a una necessità opzionale al concatenamento opzionale per accedervi così gregorian? .DateFromCoomponents è il concatenamento opzionale per scartare il valore dalle opzioni gregorian (NSCalendar). Altro per il concatenamento opzionale qui
iluvatar_GR

NSGregorianCalendar è obsoleto a partire da iOS 8, quindi usa NSCalendarIdentifierGregorian invece
Adam Knights

2
@Il senatore si lamenta perché Swift ha la struttura della data e questo codice ridichiarisce nuovamente la data come classe. Quindi dovresti fare qualcosa del tipo: 'extension Date' e 'static func'
Zaporozhchenko Oleksandr

Un promemoria sulla stringa di analisi di nuovo a Data. Una stringa "1979-07-01" con "yyyy-MM-dd" per il NSDateFormattermetodo date(from:). Ritorna nil. Ho riscontrato questo problema nel registro degli arresti anomali di fabric.io.
AechoLiu,

12

Ecco come l'ho fatto 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
    }
}

Uso:

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

Anche se sto passando '15' come giorno la data restituita dà il 14? Pensieri?
Luke Zammit,

@LukeZammit Aggiungi un fuso orario adateComponents
Adrian

provato questo senza fortuna - dateComponents.timeZone = TimeZone.current, pensieri per favore?
Luke Zammit,

5

Secondo la documentazione Apple

Esempio :

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

3
Bello ma non risponde alla domanda, la domanda non riguarda gli intervalli di tempo.
zaph,

4

In Swift 3.0 hai impostato l'oggetto data in questo modo.

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 ovunque tu voglia. Crea un file separato, chiamalo "Estensione", idk.
Zaporozhchenko Oleksandr,

2

Personalmente penso che dovrebbe essere un inizializzatore disponibile:

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

Altrimenti una stringa con un formato non valido solleverà un'eccezione.


2

Secondo la risposta di @mythz, decido di pubblicare la versione aggiornata della sua estensione usando la swift3sintassi.

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

Non uso il parsemetodo, ma se qualcuno ha bisogno, aggiornerò questo post.


1

Spesso ho bisogno di combinare i valori della data da un luogo con i valori dell'ora per un altro. Ho scritto una funzione di supporto per raggiungere questo obiettivo.

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

Secondo la Guida alla formattazione dei dati di Apple

La creazione di un formatter data non è un'operazione economica. Se è probabile che si usi frequentemente un formattatore, in genere è più efficiente memorizzare nella cache una singola istanza piuttosto che creare e smaltire più istanze. Un approccio consiste nell'utilizzare una variabile statica

E mentre sono d'accordo con @Leon che questo dovrebbe essere un inizializzatore disponibile, quando inserisci i dati di seed, potremmo averne uno che non è disponibile (proprio come c'è UIImage(imageLiteralResourceName:)).

Quindi ecco il mio approccio:

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

E ora divertiti semplicemente chiamando:

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

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.