Jak utworzyć obiekt Swift Date?

156

Jak utworzyć obiekt daty z daty w swift xcode.

np. w javascript zrobiłbyś: var day = new Date('2014-05-20');

code_cookies
źródło
10
Używać NSDatetak jak w Objective-C?
rmaddy

Odpowiedzi:

263

Swift ma swój własny Datetyp. Nie ma potrzeby używania NSDate.

Tworzenie daty i godziny w Swift

W języku Swift daty i godziny są przechowywane w 64-bitowej liczbie zmiennoprzecinkowej mierzącej liczbę sekund od daty odniesienia 1 stycznia 2001 r. O godzinie 00:00:00 czasu UTC . Wyraża się to w Datestrukturze . Poniższe informacje podają aktualną datę i godzinę:

let currentDateTime = Date()

Aby utworzyć inne daty i godziny, możesz użyć jednej z następujących metod.

Metoda 1

Jeśli znasz liczbę sekund przed lub po dacie odniesienia 2001, możesz z tego skorzystać.

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

Metoda 2

Oczywiście łatwiej byłoby użyć takich rzeczy jak lata, miesiące, dni i godziny (zamiast względnych sekund), aby utworzyć plik Date. W tym celu możesz użyć, DateComponentsaby określić składniki, a następnie Calendarutworzyć datę. To Calendardaje Datekontekst. W przeciwnym razie skąd miałoby wiedzieć, w jakiej strefie czasowej lub kalendarzu ma to wyrazić?

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

Inne skróty stref czasowych można znaleźć tutaj . Jeśli pozostawisz to pole puste, domyślnie używana jest strefa czasowa użytkownika.

Metoda 3

Najbardziej zwięzłym (ale niekoniecznie najlepszym) sposobem może być użycie DateFormatter.

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

Te normy techniczne Unicode pokazać inne formaty , że DateFormatterobsługuje.

Uwagi

Zobacz moją pełną odpowiedź, jak wyświetlić datę i godzinę w czytelnym formacie. Przeczytaj także te doskonałe artykuły:

Suragch
źródło
118

Najlepiej jest to zrobić, używając rozszerzenia istniejącej NSDateklasy.

Poniższe rozszerzenie dodaje nowy inicjator, który utworzy datę w bieżących ustawieniach regionalnych przy użyciu ciągu daty w określonym formacie.

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

Teraz możesz utworzyć NSDate w Swift, wykonując:

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

Zauważ, że ta implementacja nie buforuje NSDateFormatter, co możesz chcieć zrobić ze względu na wydajność, jeśli spodziewasz się tworzenia wielu plików NSDatew ten sposób.

Należy również pamiętać, że ta implementacja po prostu ulegnie awarii, jeśli spróbujesz zainicjować an NSDateprzez przekazanie ciągu, którego nie można poprawnie przeanalizować. Dzieje się tak z powodu wymuszonego rozpakowania wartości opcjonalnej zwróconej przez dateFromString. Jeśli chciałbyś zwrócić a nilprzy złej analizie, najlepiej byłoby użyć nieudanego inicjatora; ale nie możesz tego zrobić teraz (czerwiec 2015) z powodu ograniczeń w Swift 1.2, więc następnym najlepszym wyborem jest użycie metody fabryki klas.

Bardziej rozbudowany przykład, który dotyczy obu problemów, znajduje się tutaj: https://gist.github.com/algal/09b08515460b7bd229fa .


Aktualizacja dla 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)
    }
}
glony
źródło
9
Co zatem oznacza słowo „wygoda”?
James Burns
18
Wskazuje, że rozszerzenie udostępnia inicjator, który w rzeczywistości deleguje inicjalizację do już istniejącego wyznaczonego inicjatora, którym w tym przypadku jest inicjator NSDate.init (timeInterval:, sinceDate :). Jest to opisane na stronie 275 książki Swift Programming Language.
glony
1
Jedna wada, którą widzę w powyższym kodzie, polega na ponownym tworzeniu się za NSDateFormatterkażdym razem, gdy uzyskuje się dostęp do funkcji. Jak podaje przewodnik po formatowaniu daty , utworzenie programu formatującego datę nie jest tanią operacją . Ponadto klasa jest bezpieczna dla wątków od iOS7, więc można ją bezpiecznie używać dla wszystkich NSDaterozszerzeń.
Yevhen Dubinin
@eugenedubinin poprawnie. dlatego powiedziałem "ta implementacja nie buforuje NSDateFormatter, co możesz chcieć zrobić ze względu na wydajność".
glony
1
Jakie jest ograniczenie w Swift 1.2? Funkcja „dostępny inicjator” jest dostępna od wersji Swift 1.1 .
Franklin Yu
48

Swift nie ma własnego typu Date, ale możesz użyć istniejącego NSDatetypu Cocoa , np .:

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

Które możesz użyć na przykład:

var date = Date.parse("2014-05-20")
var date = Date.from(year: 2014, month: 05, day: 20)
mit
źródło
Musiałem zmienić wiersz: var date = gregorian? .DateFromComponents (c), ale nie rozumiem dlaczego, a parse return dateFmt.dateFromString też narzeka. Jakieś wskazówki? Ach, zmieniłem zwracany typ funkcji z NSDate na NSDate? co pozwala na kompilację. Ale nie jestem pewien, dlaczego musi mieć wartość zerową.
Senator
1
@TheSenator NSCalendar init zwraca opcjonalny (?), Więc gregoriański jest opcjonalny (?). Dostęp do opcjonalnej potrzeby Opcjonalne łańcuchowanie dostępu do niego tak gregoriańskiego? .DateFromCoomponents jest opcjonalnym łańcuchem do rozpakowywania wartości z opcji gregoriańskich (NSCalendar). Więcej informacji na temat opcjonalnego łączenia łańcuchowego tutaj
iluvatar_GR
NSGregorianCalendar jest przestarzałe od iOS 8, więc zamiast tego użyj NSCalendarIdentifierGregorian
Adam Knights
2
@The Senator Complains coz Swift ma strukturę Date, a ten kod ponownie określa Date jako klasę. Więc powinieneś zrobić coś w stylu: 'extension Date' i 'static func'
Zaporozhchenko Oleksandr
Notatka o parsowaniu ciągu z powrotem do daty. Ciąg „1979-07-01” z „rrrr-MM-dd” dla NSDateFormattermetody date(from:). Wraca nil. Znalazłem ten problem w dzienniku awarii fabric.io.
AechoLiu
12

Oto, jak zrobiłem to w 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
    }
}

Stosowanie:

let marsOpportunityLaunchDate = Date.from(year: 2003, month: 07, day: 07)
Adrian
źródło
Chociaż mijam „15” jako dzień, data zwrotu to 14? Myśli?
Luke Zammit
@LukeZammit Dodaj strefę czasową dodateComponents
Adrian
próbowałem tego bez powodzenia - dateComponents.timeZone = TimeZone.current, proszę o przemyślenia?
Luke Zammit
5

Zgodnie z dokumentacją Apple

Przykład:

var myObject = NSDate()
let futureDate = myObject.dateByAddingTimeInterval(10)
let timeSinceNow = myObject.timeIntervalSinceNow
Kevin Machado
źródło
3
Fajnie ale nie odpowiada na pytanie, pytanie nie dotyczy przedziałów czasowych.
zaph
4

W Swift 3.0 ustawiłeś w ten sposób obiekt daty.

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
źródło
@ Zonker.in.Geneva, gdziekolwiek chcesz. Utwórz osobny plik, nazwij go „Rozszerzenie”, idk.
Zaporozhchenko Oleksandr
2

Osobiście uważam, że powinien to być dostępny inicjalizator:

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

W przeciwnym razie ciąg o nieprawidłowym formacie zgłosi wyjątek.

Leon
źródło
2

Zgodnie z odpowiedzią @mythz zdecydowałem się opublikować zaktualizowaną wersję jego rozszerzenia przy użyciu swift3składni.

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

Nie używam parsemetody, ale jak ktoś będzie potrzebował, zaktualizuję ten post.

Hamsternik
źródło
1

Często mam potrzebę łączenia wartości dat z jednego miejsca z wartościami czasu w innym. Napisałem funkcję pomocniczą, aby to osiągnąć.

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
źródło
0

Zgodnie z instrukcją formatowania danych firmy Apple

Tworzenie programu formatującego datę nie jest tanią operacją. Jeśli często używasz programu formatującego, zwykle bardziej wydajne jest buforowanie pojedynczego wystąpienia niż tworzenie i usuwanie wielu wystąpień. Jednym podejściem jest użycie zmiennej statycznej

I chociaż zgadzam się z @Leon, że powinien to być dostępny inicjator, kiedy wprowadzasz dane początkowe, możemy mieć taki, który nie jest dostępny (tak jak jest UIImage(imageLiteralResourceName:)).

Oto moje podejście:

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

A teraz ciesz się po prostu dzwoniąc:

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

// When parsing data
guard let date = Date(yyyyMMdd: "2020-03-30") else { return nil }
Nycen
źródło