¿Cómo se crea un objeto Swift Date?

156

¿Cómo se crea un objeto de fecha a partir de una fecha en xcode rápido?

por ejemplo, en javascript harías: var day = new Date('2014-05-20');

code_cookies
fuente
10
Usar NSDatecomo en Objective-C?
rmaddy

Respuestas:

263

Swift tiene su propio Datetipo. No es necesario usar NSDate.

Crear una fecha y hora en Swift

En Swift, las fechas y horas se almacenan en un número de coma flotante de 64 bits que mide la cantidad de segundos desde la fecha de referencia del 1 de enero de 2001 a las 00:00:00 UTC . Esto se expresa en la Dateestructura . Lo siguiente le daría la fecha y hora actuales:

let currentDateTime = Date()

Para crear otras fechas y horas, puede usar uno de los siguientes métodos.

Método 1

Si conoce el número de segundos antes o después de la fecha de referencia de 2001, puede usar eso.

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

Método 2

Por supuesto, sería más fácil usar cosas como años, meses, días y horas (en lugar de segundos relativos) para hacer un Date. Para esto, puede usar DateComponentspara especificar los componentes y luego Calendarcrear la fecha. El Calendarda el Datecontexto. De lo contrario, ¿cómo sabría en qué zona horaria o calendario expresarlo?

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

Se pueden encontrar otras abreviaturas de zona horaria Aquí . Si lo deja en blanco, el valor predeterminado es usar la zona horaria del usuario.

Método 3

La forma más sucinta (pero no necesariamente la mejor) podría ser usar DateFormatter.

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

Los estándares técnicos Unicode muestran otros formatos queDateFormatter soporta.

Notas

Vea mi respuesta completa sobre cómo mostrar la fecha y la hora en un formato legible. Lea también estos excelentes artículos:

Suragch
fuente
118

Esto se hace mejor usando una extensión de la NSDateclase existente .

La siguiente extensión agrega un nuevo inicializador que creará una fecha en el entorno local actual utilizando la cadena de fecha en el formato que especificó.

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

Ahora puede crear un NSDate de Swift simplemente haciendo:

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

Tenga en cuenta que esta implementación no almacena en caché el NSDateFormatter, que es posible que desee hacer por razones de rendimiento si espera crear muchos correos electrónicos de NSDateesta manera.

Tenga en cuenta también que esta implementación simplemente se bloqueará si intenta inicializar NSDatepasando una cadena que no se puede analizar correctamente. Esto se debe al desenvolvimiento forzado del valor opcional devuelto por dateFromString. Si desea devolver nilun análisis incorrecto, lo ideal sería utilizar un inicializador defectuoso; pero no puede hacerlo ahora (junio de 2015), debido a una limitación en Swift 1.2, por lo que su siguiente mejor opción es usar un método de fábrica de clase.

Un ejemplo más elaborado, que aborda ambos problemas, está aquí: https://gist.github.com/algal/09b08515460b7bd229fa .


Actualización para 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)
    }
}
alga
fuente
9
Entonces, ¿cuál es la palabra "conveniencia" hacer aquí?
James Burns
18
Indica que la extensión proporciona un inicializador que de hecho delega la inicialización a un inicializador designado ya existente, que en este caso es el inicializador NSDate.init (timeInterval:, sinceDate :). Esto se describe en la página 275 del libro The Swift Programming Language.
algal
1
Una falla que veo en el código anterior es volver a crear NSDateFormattercada vez que se accede a la función. Como indica la Guía de formato de fecha , la creación de un formateador de fecha no es una operación barata . Además, la clase es segura para subprocesos desde iOS7, por lo que se puede usar con seguridad para todas las NSDateextensiones.
Yevhen Dubinin
@eugenedubinin correcto. Es por eso que dije "esta implementación no almacena en caché el NSDateFormatter, que es posible que desee hacer por razones de rendimiento".
algal
1
¿Cuál es la limitación en Swift 1.2? La función "inicializador disponible " ha estado disponible desde Swift 1.1 .
Franklin Yu
48

Swift no tiene su propio tipo de fecha, pero puede usar el NSDatetipo de cacao existente , por ejemplo:

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 puedes usar como:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
mythz
fuente
Tuve que cambiar la línea: var date = gregorian? .DateFromComponents (c) pero no entiendo por qué, y el análisis return dateFmt.dateFromString también se queja. ¿Alguna pista? Ah, ¿he cambiado el tipo de retorno de la función de NSDate a NSDate? lo que le permite compilar. Pero no estoy seguro de por qué necesita ser anulable.
El senador
1
@TheSenator NSCalendar init devuelve un opcional (?) Por lo que gregorian es opcional (?). Acceder a una necesidad opcional de encadenamiento opcional para acceder a él de manera gregorian? .DateFromCoomponents es el encadenamiento opcional para desenvolver el valor de las opciones gregorian (NSCalendar). Más para encadenamiento opcional aquí
iluvatar_GR
NSGregorianCalendar está en desuso a partir de iOS 8, así que use NSCalendarIdentifierGregorian en su lugar
Adam Knights
2
@El senador se queja porque Swift tiene estructura de fecha, y este código vuelve a declarar la fecha nuevamente como clase. Entonces deberías hacer algo como: 'fecha de extensión' y 'función estática'
Zaporozhchenko Oleksandr
Una nota sobre la secuencia de análisis de nuevo a la fecha. Una cadena "1979-07-01" con "aaaa-MM-dd" para el NSDateFormattermétodo date(from:). Vuelve nil. Encontré este problema en el registro de fallas de fabric.io.
AechoLiu
12

Así es como lo hice en 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)
Adrian
fuente
Aunque estoy pasando '15' como un día, la fecha devuelta da el 14? Pensamientos?
Luke Zammit
@LukeZammit Agregue una zona horaria adateComponents
Adrian
probé esto sin suerte - dateComponents.timeZone = TimeZone.current, ¿pensamientos por favor?
Luke Zammit
5

De acuerdo con la documentación de Apple

Ejemplo:

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Kevin Machado
fuente
3
Agradable pero no responde la pregunta, la pregunta no es sobre intervalos de tiempo.
zaph
4

En Swift 3.0, ha establecido el objeto de fecha de esta manera.

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
fuente
@ Zonker.in.Geneva donde quieras. Cree un archivo separado, llámelo "Extensión", idk.
Zaporozhchenko Oleksandr
2

Personalmente, creo que debería ser un inicializador fallable:

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

De lo contrario, una cadena con un formato no válido generará una excepción.

León
fuente
2

Según la respuesta de @mythz, decido publicar una versión actualizada de su extensión usando la swift3sintaxis.

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

No uso el parsemétodo, pero si alguien lo necesita, actualizaré esta publicación.

Hamsternik
fuente
1

A menudo tengo la necesidad de combinar valores de fecha de un lugar con valores de tiempo para otro. Escribí una función auxiliar para lograr esto.

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

De acuerdo con la Guía de formato de datos de Apple

Crear un formateador de fecha no es una operación barata. Si es probable que use un formateador con frecuencia, generalmente es más eficiente almacenar en caché una sola instancia que crear y eliminar múltiples instancias. Un enfoque es usar una variable estática

Y si bien estoy de acuerdo con @Leon en que este debería ser un inicializador de error, cuando ingresas datos iniciales, podríamos tener uno que no esté disponible (tal como lo hay UIImage(imageLiteralResourceName:)).

Así que aquí está mi enfoque:

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

Y ahora disfruta simplemente llamando:

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

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