Como você cria um objeto Swift Date?

156

Como você cria um objeto de data a partir de uma data no xcode rápido.

por exemplo, em javascript, você faria: var day = new Date('2014-05-20');

code_cookies
fonte
10
Use NSDatecomo no Objective-C?
rmaddy

Respostas:

263

Swift tem seu próprio Datetipo. Não há necessidade de usar NSDate.

Criando uma data e hora no Swift

No Swift, as datas e horas são armazenadas em um número de ponto flutuante de 64 bits, medindo o número de segundos desde a data de referência de 1 de janeiro de 2001 às 00:00:00 UTC . Isso é expresso na Dateestrutura . O seguinte forneceria a data e hora atuais:

let currentDateTime = Date()

Para criar outras datas e horários, você pode usar um dos seguintes métodos.

Método 1

Se você souber o número de segundos antes ou depois da data de referência de 2001, poderá usá-lo.

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

Método 2

Obviamente, seria mais fácil usar coisas como anos, meses, dias e horas (em vez de segundos relativos) para criar um Date. Para isso, você pode usar DateComponentspara especificar os componentes e, em seguida, Calendarcriar a data. O Calendardá o Datecontexto. Caso contrário, como saberia em que fuso horário ou calendário expressá-lo?

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

Outras abreviações de fuso horário podem ser encontradas aqui . Se você deixar em branco, o padrão é usar o fuso horário do usuário.

Método 3

A maneira mais sucinta (mas não necessariamente a melhor) poderia ser usar DateFormatter.

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

As normas técnicas Unicode mostrar outros formatos que DateFormattersuporta.

Notas

Veja minha resposta completa sobre como exibir a data e a hora em um formato legível. Leia também estes excelentes artigos:

Suragch
fonte
118

É melhor fazer isso usando uma extensão para a NSDateclasse existente .

A extensão a seguir adiciona um novo inicializador que criará uma data no local atual usando a sequência de datas no formato especificado.

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

Agora você pode criar um NSDate a partir do Swift apenas fazendo:

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

Observe que esta implementação não armazena em cache o NSDateFormatter, o que você pode querer fazer por motivos de desempenho, se espera criar muitos NSDates dessa maneira.

Observe também que essa implementação simplesmente trava se você tentar inicializar um NSDatepassando uma string que não pode ser analisada corretamente. Isso ocorre devido ao desempacotamento forçado do valor opcional retornado por dateFromString. Se você deseja retornar niluma análise ruim, o ideal seria usar um inicializador com falha; mas você não pode fazer isso agora (junho de 2015), devido a uma limitação no Swift 1.2, então a sua melhor opção é usar um método de fábrica de classe.

Um exemplo mais elaborado, que aborda os dois problemas, está aqui: https://gist.github.com/algal/09b08515460b7bd229fa .


Atualização para o 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)
    }
}
algal
fonte
9
Então, o que a palavra "conveniência" faz aqui?
James Burns
18
Indica que a extensão está fornecendo um inicializador que de fato delega a inicialização para um inicializador designado já existente, que nesse caso é o inicializador NSDate.init (timeInterval:, sinceDate :). Isso está descrito na página 275 do livro The Swift Programming Language.
amigos estão
1
Uma falha que vejo no código acima é recriada NSDateFormattercada vez que a função é acessada. Como indica o Guia de formatação de data , criar um formatador de data não é uma operação barata . Além disso, a classe é segura para threads desde o iOS7, para que você possa usá-la com segurança em todas as NSDateextensões.
Yevhen Dubinin
@eugenedubinin correct. foi por isso que disse: "essa implementação não armazena em cache o NSDateFormatter, o que você pode querer fazer por motivos de desempenho".
algal.
1
Qual é a limitação no Swift 1.2? O recurso "inicializador disponível " está disponível desde o Swift 1.1 .
Franklin Yu
48

Swift não tem seu próprio tipo de data, mas você deve usar o NSDatetipo de cacau existente , por exemplo:

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 você pode usar como:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
mythz
fonte
Eu tive que mudar a linha: var date = gregorian? .DateFromComponents (c), mas não entendo o porquê, e a análise de retorno dateFmt.dateFromString também está reclamando. Alguma pista? Ah, eu mudei o tipo de retorno da função de NSDate para NSDate? o que permite compilar. Mas não sei por que ele precisa ser anulável.
O senador
1
O init do TheSCenendar NSCalendar retorna um opcional (?), Portanto, gregoriano é um opcional (?). Acessar uma necessidade opcional de Encadeamento opcional para acessá-lo de forma gregoriana? .DateFromCoomponents é o encadeamento opcional para desembrulhar o valor das opções gregorian (NSCalendar). Mais para encadeamento opcional aqui
iluvatar_GR 4/15/15
NSGregorianCalendar está obsoleto a partir do iOS 8 por isso use NSCalendarIdentifierGregorian vez
Adam cavaleiros
2
@ O Senador reclama que o Swift possui a estrutura Date e esse código redefine Data novamente como classe. Então, você deve fazer algo como: 'extension Date' e 'static func' #
Zaporozhchenko Oleksandr
Uma nota sobre a sequência de análise de volta à Data. Uma cadeia de caracteres "1979/07/01" com "aaaa-MM-dd" para NSDateFormattermétodo date(from:). Retorna nil. Encontrei esse problema no log de falha do fabric.io.
AechoLiu
12

Aqui está como eu fiz isso no 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
fonte
Embora eu esteja passando '15' como um dia, a data retornada dá o 14º? Pensamentos?
Luke Zammit
@LukeZammit Adicione um fuso horário paradateComponents
Adrian
tentei isso sem sorte - dateComponents.timeZone = TimeZone.current, pensamentos por favor?
Luke Zammit
5

De acordo com a documentação da Apple

Exemplo:

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Kevin Machado
fonte
3
Bom, mas não responde à pergunta, a questão não é sobre intervalos de tempo.
Zaph 06/06
4

No Swift 3.0, você definiu o objeto de data dessa maneira.

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
fonte
@ Zonker.in.Geneva onde você quiser. Crie um arquivo separado, chame-o de "Extensão", idk.
Zaporozhchenko Oleksandr
2

Pessoalmente, acho que deve ser um inicializador disponível:

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

Caso contrário, uma sequência com um formato inválido gerará uma exceção.

Leon
fonte
2

De acordo com a resposta @mythz, decido postar a versão atualizada de sua extensão usando a swift3sintaxe.

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

Eu não uso o parsemétodo, mas se alguém precisar, atualizarei esta postagem.

Hamsternik
fonte
1

Muitas vezes, preciso combinar valores de data de um local com valores de hora para outro. Eu escrevi uma função auxiliar para fazer isso.

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

De acordo com o Guia de formatação de dados da Apple

Criar um formatador de datas não é uma operação barata. Se é provável que você use um formatador com frequência, geralmente é mais eficiente armazenar em cache uma única instância do que criar e descartar várias instâncias. Uma abordagem é usar uma variável estática

E, embora eu concorde com o @Leon que este deve ser um inicializador disponível, quando você insere dados iniciais, poderíamos ter um que não esteja disponível (exatamente como existe UIImage(imageLiteralResourceName:)).

Então, aqui está a minha abordagem:

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 agora aproveite simplesmente ligando para:

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

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