Як створити об’єкт Swift Date?


156

Як створити об’єкт дати з дати у швидкому xcode.

наприклад, у JavaScript ви зробите: var day = new Date('2014-05-20');


10
Використовувати так NSDateсамо, як і в Objective-C?
— rmaddy

Відповіді:


263

Свіфт має свій 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підставки.

Примітки

Дивіться мою повну відповідь про те, як відобразити дату та час у читаному форматі. Читайте також ці чудові статті:


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. Якщо ви хочете повернути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 :). Це описано на сторінці 275 книги «Мова швидкого програмування».
— algal

1
Один недолік, який я бачу у наведеному вище коді, відновлюється NSDateFormatterкожного разу, коли доступ до функції. Як зазначено в Посібнику з форматування дат, Створення форматера дати не є дешевою операцією . Крім того, клас є безпечним для потоків від iOS7, тому можна сміливо використовувати його для всіх NSDateрозширень.
— Євген Дубінін

@eugenedubinin правильний. ось чому я сказав: "Ця реалізація не кешує NSDateFormatter, що ви можете зробити з міркувань продуктивності".
— algal

1
Яке обмеження у Swift 1.2? Функція "недоступний ініціалізатор" доступна з Swift 1.1 .
— Франклін Ю

48

У Swift немає власного типу дати, але ви можете використовувати існуючий NSDateтип какао , наприклад:

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)

Мені довелося змінити рядок: var date = gregorian? .DateFromComponents (c), але я не розумію, чому, і дата повернення синтаксичного аналізуFmt.dateFromString теж скаржиться. Будь-які підказки? Ах, я змінив тип повернення функції з NSDate на NSDate? що дозволяє компілювати. Але я не впевнений, чому це потрібно звести нанівець.
— Сенатор

1
@TheSenator NSCalendar init повертає необов’язковий (?), Тому gregorian є необов'язковим (?). Доступ до необов'язкової потреби до Необов’язкового ланцюга для доступу до нього настільки григоріанською? Більше про факультативне
— ланцюг

NSGregorianCalendar застаріло, як на iOS 8, тому замість цього використовуйте NSCalendarIdentifierGregorian
— Adam Knights

2
@ Senator Complains coz Swift має структуру 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, думки, будь ласка?
— Люк Замміт


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

@ Zonker.in.Geneva куди завгодно. Створіть окремий файл, назвіть його "Розширення", 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метод, але якщо комусь потрібно, я оновлю цю публікацію.


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 }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.