Как вы создаете объект Swift Date?

157

Как вы создаете объект даты из даты в Swift Xcode.

Например, в JavaScript вы бы сделали: var day = new Date('2014-05-20');

code_cookies
источник
10
Использовать так NSDateже, как в Objective-C?
rmaddy

Ответы:

263

Swift имеет свой Dateтип. Не нужно использовать NSDate.

Создание даты и времени в Swift

В Swift даты и время хранятся в 64-битном числе с плавающей запятой, которое измеряет количество секунд с базовой даты 1 января 2001 года в 00:00:00 UTC . Это выражено в Dateструктуре . Следующее даст вам текущую дату и время:

let currentDateTime = Date()

Для создания других дат-времени вы можете использовать один из следующих методов.

Способ 1

Если вам известно количество секунд до или после контрольной даты 2001 года, вы можете использовать ее.

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

Способ 2

Конечно, было бы проще использовать такие вещи, как годы, месяцы, дни и часы (а не относительные секунды) для создания Date. Для этого вы можете использовать, DateComponentsчтобы указать компоненты, а затем Calendarсоздать дату. Это Calendarдает Dateконтекст. Иначе как бы он узнал, в каком часовом поясе или календаре это выражать?

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

Другие сокращения часовых поясов можно найти здесь . Если вы оставите это поле пустым, по умолчанию будет использоваться часовой пояс пользователя.

Способ 3

Наиболее кратким способом (но не обязательно лучшим) может быть использование DateFormatter.

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

В технических стандартах Unicode показать другие форматы , что DateFormatterподставки.

Ноты

Смотрите мой полный ответ о том, как отображать дату и время в удобочитаемом формате. Также прочитайте эти отличные статьи:

Suragch
источник
118

Лучше всего это сделать с помощью расширения существующего NSDateкласса.

Следующее расширение добавляет новый инициализатор, который будет создавать дату в текущей локали, используя строку даты в указанном вами формате.

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

Теперь вы можете создать NSDate из Swift, просто выполнив:

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

Обратите внимание, что эта реализация не кэширует NSDateFormatter, что может потребоваться из соображений производительности, если вы собираетесь создавать множество NSDates таким способом.

Также обратите внимание, что эта реализация просто потерпит крах, если вы попытаетесь инициализировать NSDate, передав строку, которая не может быть проанализирована правильно. Это связано с принудительной разверткой необязательного значения, возвращаемого функцией dateFromString. Если вы хотите вернуть a nilпри плохом разборе, в идеале вы должны использовать неисправный инициализатор; но вы не можете сделать это сейчас (июнь 2015 г.) из-за ограничений в Swift 1.2, поэтому следующий ваш лучший выбор - использовать метод фабрики классов.

Более сложный пример, который решает обе проблемы, находится здесь: https://gist.github.com/algal/09b08515460b7bd229fa .


Обновление для 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
Так что же здесь означает слово «удобство»?
Джеймс Бернс
18
Это указывает, что расширение предоставляет инициализатор, который фактически делегирует инициализацию уже существующему назначенному инициализатору, который в этом случае является NSDate.init (timeInterval:, sinceDate :) initializer. Это описано на странице 275 книги Swift Programming Language.
Водоросль
1
Один недостаток, который я вижу в приведенном выше коде, - повторное создание при NSDateFormatterкаждом доступе к функции. Как говорится в Руководстве по форматированию даты Создание средства форматирования даты не является дешевой операцией . Кроме того, этот класс является поточно-ориентированным, начиная с iOS7, поэтому его можно безопасно использовать для всех NSDateрасширений.
Евгений Дубинин
@eugenedubinin правильно. Вот почему я сказал, что «эта реализация не кэширует NSDateFormatter, что может потребоваться для повышения производительности».
водорослевые
1
Какое ограничение в Swift 1.2? Функция «сбой инициализатора» доступна с версии Swift 1.1 .
Франклин Ю
48

У Swift нет собственного типа Date, но вы должны использовать существующий NSDateтип Cocoa , например:

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

Который вы можете использовать как:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
mythz
источник
Мне пришлось изменить строку: var date = gregorian? .DateFromComponents (c), но я не понимаю, почему, и синтаксический анализ dateFmt.dateFromString тоже жалуется. Есть какие-нибудь подсказки? Ах, я изменил тип возврата функции с NSDate на NSDate? что позволяет компилировать. Но я не уверен, почему это должно быть обнуляемым.
Сенатор
1
@TheSenator NSCalendar init возвращает необязательный (?), Поэтому григорианский является необязательным (?). Доступ к необязательному необязательному необязательному объединению в цепочку для получения такого григорианского доступа. .DateFromCoomponents - это необязательное сцепление, позволяющее развернуть значение из опций григорианского (NSCalendar). Больше для Опционального сцепления здесь
iluvatar_GR
Начиная с iOS 8, NSGregorianCalendar устарел, поэтому вместо него следует использовать NSCalendarIdentifierGregorian
Adam Knights,
2
@ Сенатор жалуется, потому что Swift имеет структуру Date, и этот код снова объявляет Date как класс. Таким образом, вы должны сделать что-то вроде: «Дата продления» и «Статическая функция»
Запорожченко Александр
Памятка о разборе строки назад к дате. Строка "1979-07-01" с "yyyy-MM-dd" для NSDateFormatterметода date(from:). Это возвращается nil. Я нашел эту проблему в журнале сбоев fabric.io.
AechoLiu
12

Вот как я это сделал в 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
    }
}

Использование:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)
Адриан
источник
Хотя я пропускаю '15' как день, возвращаемая дата дает 14-е? Мысли?
Люк Заммит
@LukeZammit Добавить часовой поясdateComponents
Адриан
попробовал это без удачи - dateComponents.timeZone = TimeZone.current, мысли, пожалуйста?
Люк Заммит
5

Согласно документации Apple

Пример :

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Кевин Мачадо
источник
3
Приятно, но не отвечает на вопрос, вопрос не о временных интервалах.
Zaph
4

В Swift 3.0 вы установили объект даты для этого способа.

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
источник
@ Zonker.in.Geneva везде, где вы хотите. Создайте отдельный файл, назовите его «Extension», idk.
Запорожченко Александр
2

Лично я думаю, что это должен быть неудачный инициализатор:

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

В противном случае строка с неверным форматом вызовет исключение.

Леон
источник
2

Согласно ответу @mythz, я решил опубликовать обновленную версию его расширения с использованием swift3синтаксиса.

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

Я не использую parseметод, но если кому-то понадобится, я обновлю этот пост.

Hamsternik
источник
1

Мне часто нужно комбинировать значения даты из одного места со значениями времени для другого. Я написал вспомогательную функцию для достижения этой цели.

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

Согласно руководству по форматированию данных Apple

Создание средства форматирования даты - не дешевая операция. Если вы, вероятно, будете часто использовать средство форматирования, обычно более эффективно кэшировать один экземпляр, чем создавать и распоряжаться несколькими экземплярами. Одним из подходов является использование статической переменной

И хотя я согласен с @Leon, что это должен быть неудачный инициализатор, когда вы вводите начальные данные, у нас может быть тот, который не является неисправным (как и есть UIImage(imageLiteralResourceName:)).

Итак, вот мой подход:

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

А теперь наслаждайтесь простым звонком:

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

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Arnaud
источник