Як створити об’єкт 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.