Comment créer un objet Swift Date?

157

Comment créer un objet date à partir d'une date dans swift xcode.

par exemple en javascript vous feriez: var day = new Date('2014-05-20');

code_cookies
la source
10
Utiliser NSDatecomme en Objective-C?
rmaddy

Réponses:

263

Swift a son propre Datetype. Pas besoin d'utiliser NSDate.

Création d'une date et d'une heure dans Swift

Dans Swift, les dates et les heures sont stockées dans un nombre à virgule flottante de 64 bits mesurant le nombre de secondes depuis la date de référence du 1er janvier 2001 à 00:00:00 UTC . Cela s'exprime dans la Datestructure . Ce qui suit vous donnerait la date et l'heure actuelles:

let currentDateTime = Date()

Pour créer d'autres dates-heures, vous pouvez utiliser l'une des méthodes suivantes.

Méthode 1

Si vous connaissez le nombre de secondes avant ou après la date de référence 2001, vous pouvez l'utiliser.

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

Méthode 2

Bien sûr, il serait plus facile d'utiliser des éléments tels que des années, des mois, des jours et des heures (plutôt que des secondes relatives) pour créer un fichier Date. Pour cela, vous pouvez utiliser DateComponentspour spécifier les composants, puis Calendarpour créer la date. Le Calendardonne le Datecontexte. Sinon, comment pourrait-il savoir dans quel fuseau horaire ou calendrier l'exprimer?

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

D'autres abréviations de fuseau horaire peuvent être trouvées ici . Si vous laissez ce champ vide, la valeur par défaut est d'utiliser le fuseau horaire de l'utilisateur.

Méthode 3

La manière la plus succincte (mais pas nécessairement la meilleure) pourrait être d'utiliser DateFormatter.

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

Les normes techniques Unicode montrent d'autres formats qui DateFormatterprennent en charge.

Remarques

Voir ma réponse complète pour savoir comment afficher la date et l'heure dans un format lisible. Lisez également ces excellents articles:

Suragch
la source
118

Le mieux est d'utiliser une extension de la NSDateclasse existante .

L'extension suivante ajoute un nouvel initialiseur qui créera une date dans les paramètres régionaux actuels en utilisant la chaîne de date au format que vous avez spécifié.

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

Vous pouvez maintenant créer un NSDate à partir de Swift simplement en faisant:

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

Veuillez noter que cette implémentation ne met pas en cache NSDateFormatter, ce que vous voudrez peut-être faire pour des raisons de performances si vous prévoyez de créer de nombreux NSDates de cette manière.

Veuillez également noter que cette implémentation plantera simplement si vous essayez d'initialiser un NSDateen passant une chaîne qui ne peut pas être analysée correctement. Cela est dû au déroulement forcé de la valeur facultative renvoyée par dateFromString. Si vous vouliez renvoyer un nilsur les mauvaises analyses, vous utiliseriez idéalement un initialiseur qui échoue; mais vous ne pouvez pas le faire maintenant (juin 2015), à cause d'une limitation dans Swift 1.2, alors le meilleur choix est d'utiliser une méthode de fabrique de classes.

Un exemple plus élaboré, qui résout les deux problèmes, est ici: https://gist.github.com/algal/09b08515460b7bd229fa .


Mise à jour pour 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)
    }
}
algue
la source
9
Alors, quel est le mot «commodité» ici?
james Burns
18
Il indique que l'extension fournit un initialiseur qui délègue en fait l'initialisation à un initialiseur désigné déjà existant, qui dans ce cas est l'initialiseur NSDate.init (timeInterval:, sinceDate :). Ceci est décrit à la page 275 du livre The Swift Programming Language.
algal
1
Une faille que je vois dans le code ci-dessus est la recréation à NSDateFormatterchaque fois que la fonction est accédée. Comme l' indique le guide de mise en forme de date , la création d'un formateur de date n'est pas une opération bon marché . De plus, la classe est thread-safe depuis iOS7, donc on peut l'utiliser en toute sécurité pour toutes les NSDateextensions.
Yevhen Dubinin
@eugenedubinin est correct. c'est pourquoi j'ai dit "cette implémentation ne cache pas le NSDateFormatter, ce que vous voudrez peut-être faire pour des raisons de performances".
algal
1
Quelle est la limitation de Swift 1.2? La fonction "initialiseur disponible " est disponible depuis Swift 1.1 .
Franklin Yu
48

Swift n'a pas son propre type de date, mais vous devez utiliser le NSDatetype Cocoa existant , par exemple:

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

Que vous pouvez utiliser comme:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
Mythz
la source
J'ai dû changer la ligne: var date = gregorian? .DateFromComponents (c) mais je ne comprends pas pourquoi, et l'analyse de retour dateFmt.dateFromString se plaint aussi. Des indices? Ah, j'ai changé le type de retour de la fonction de NSDate à NSDate? ce qui lui permet de compiler. Mais je ne sais pas pourquoi il doit être nullable.
Le sénateur
1
@TheSenator NSCalendar init renvoie une option (?) Donc grégorien est une option (?). Accéder à un besoin optionnel de chaînage optionnel pour y accéder de manière grégorienne? .DateFromCoomponents est le chaînage optionnel pour décompresser la valeur des options grégoriennes (NSCalendar). Plus pour le chaînage facultatif ici
iluvatar_GR
NSGregorianCalendar est obsolète à partir d'iOS 8, utilisez donc NSCalendarIdentifierGregorian à la place
Adam Knights
2
@Le sénateur se plaint parce que Swift a une structure Date, et ce code redéclarera la date en tant que classe. Donc, vous devriez faire quelque chose comme: 'extension Date' et 'static func'
Zaporozhchenko Oleksandr
Un mémo sur l'analyse de la chaîne de retour à Date. Une chaîne "1979-07-01" avec "aaaa-MM-jj" pour la NSDateFormatterméthode date(from:). Il revient nil. J'ai trouvé ce problème dans le journal des plantages de fabric.io.
AechoLiu
12

Voici comment je l'ai fait dans 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
    }
}

Usage:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)
Adrian
la source
Bien que je passe '15' comme jour, la date de retour donne le 14? Pensées?
Luke Zammit
@LukeZammit Ajouter un fuseau horaire àdateComponents
Adrian
essayé sans chance - dateComponents.timeZone = TimeZone.current, des pensées s'il vous plaît?
Luke Zammit
5

Selon la documentation Apple

Exemple :

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Kevin Machado
la source
3
Sympa mais ne répond pas à la question, la question ne concerne pas les intervalles de temps.
zaph
4

Dans Swift 3.0, vous avez défini l'objet de date de cette manière.

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
la source
@ Zonker.in.Geneva partout où vous voulez. Créez un fichier séparé, appelez-le "Extension", idk.
Zaporozhchenko Oleksandr
2

Personnellement, je pense que cela devrait être un initialiseur disponible:

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

Sinon, une chaîne avec un format non valide lèvera une exception.

Léon
la source
2

Selon la réponse @mythz, je décide de publier une version mise à jour de son extension en utilisant la swift3syntaxe.

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

Je n'utilise pas de parseméthode, mais si quelqu'un en a besoin, je mettrai à jour ce post.

Hamsternik
la source
1

J'ai souvent besoin de combiner des valeurs de date d'un endroit avec des valeurs de temps pour un autre. J'ai écrit une fonction d'aide pour accomplir cela.

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
la source
0

Selon le guide de formatage des données d'Apple

La création d'un formateur de date n'est pas une opération bon marché. Si vous êtes susceptible d'utiliser fréquemment un formateur, il est généralement plus efficace de mettre en cache une seule instance que de créer et de supprimer plusieurs instances. Une approche consiste à utiliser une variable statique

Et bien que je sois d'accord avec @Leon sur le fait que cela devrait être un initialiseur disponible, lorsque vous entrez des données de départ, nous pourrions en avoir un qui ne soit pas disponible (tout comme il y en a UIImage(imageLiteralResourceName:)).

Alors voici mon approche:

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

Et maintenant, profitez simplement d'appels:

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

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