คุณจะสร้างวัตถุ Swift Date ได้อย่างไร


156

คุณจะสร้างวัตถุวันที่จากวันที่ใน swift xcode ได้อย่างไร

เช่นใน javascript ที่คุณต้องการ: var day = new Date('2014-05-20');


10
ใช้NSDateเช่นเดียวกับใน Objective-C?
rmaddy

คำตอบ:


263

สวิฟท์มีDateประเภทของตัวเอง NSDateไม่จำเป็นต้องใช้

การสร้างวันที่และเวลาอย่างรวดเร็ว

สวิฟท์ในวันที่และเวลาจะถูกเก็บไว้ในหลายจุด 64 บิตลอยวัดจำนวนวินาทีนับตั้งแต่วันที่อ้างอิงวันที่ 1 มกราคม 2001 00:00:00 UTC นี้จะแสดงในโครงสร้างDate ต่อไปนี้จะให้วันที่และเวลาปัจจุบันกับคุณ:

let currentDateTime = Date()

สำหรับการสร้างวันที่อื่นคุณสามารถใช้หนึ่งในวิธีต่อไปนี้

วิธีที่ 1

หากคุณทราบจำนวนวินาทีก่อนหรือหลังวันที่อ้างอิงปี 2544 คุณสามารถใช้จำนวนนั้นได้

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 ซึ่งคุณอาจต้องการทำเพื่อเหตุผลด้านประสิทธิภาพหากคุณคาดว่าจะสร้างหลาย ๆNSDateวิธีด้วยวิธีนี้

โปรดทราบว่าการใช้งานนี้จะล้มเหลวหากคุณพยายามเริ่มต้นNSDateด้วยการส่งผ่านสตริงที่ไม่สามารถแยกวิเคราะห์ได้อย่างถูกต้อง dateFromStringนี้เป็นเพราะการแกะบังคับของค่าตัวเลือกที่ส่งกลับโดย หากคุณต้องการคืนค่าการnilแยกวิเคราะห์ที่ไม่ดีคุณควรใช้ initializer ที่ล้มเหลว แต่คุณไม่สามารถทำได้ในตอนนี้ (มิถุนายน 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
แล้วคำว่า "ความสะดวกสบาย" ทำอะไรที่นี่?
james Burns

18
บ่งชี้ว่าส่วนขยายกำลังจัดเตรียมเครื่องมือเริ่มต้นที่ในความเป็นจริงมอบหมายการเริ่มต้นให้กับเครื่องมือกำหนดค่าเริ่มต้นที่มีอยู่แล้วซึ่งในกรณีนี้คือ NSDate.init (timeInterval:, เนื่องจาก DATE :) initializer อธิบายไว้ในหน้า 275 ของหนังสือภาษา Swift Programming
algal

1
ข้อบกพร่องหนึ่งที่ฉันเห็นในรหัสข้างต้นคือการสร้างใหม่NSDateFormatterทุกครั้งที่เข้าถึงฟังก์ชัน ในฐานะที่เป็นวันที่จัดรูปแบบคู่มือฯสร้างวันที่จัดรูปแบบไม่ได้ดำเนินการราคาถูก นอกจากนี้คลาสยังปลอดภัยสำหรับเธรดตั้งแต่ iOS7 ดังนั้นหนึ่งสามารถใช้กับNSDateส่วนขยายทั้งหมดได้อย่างปลอดภัย
Yevhen Dubinin

@eugenedubinin ถูกต้อง นั่นเป็นเหตุผลที่ฉันพูดว่า "การใช้งานนี้ไม่ได้แคช NSDateFormatter ซึ่งคุณอาจต้องการทำเพื่อเหตุผลด้านประสิทธิภาพ"
algal

1
ข้อ จำกัด ใน Swift 1.2 คืออะไร คุณลักษณะ "initializer failable" ได้รับมีตั้งแต่สวิฟท์ 1.1
Franklin Yu

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)

ฉันต้องเปลี่ยนบรรทัด: var date = gregorian? .DateFromComponents (c) แต่ฉันไม่เข้าใจว่าทำไมและวันที่กลับมาแจงแยกวิเคราะห์ Fmt.dateFromString ก็บ่นเช่นกัน เบาะแสใด ๆ อ่าฉันเปลี่ยนประเภทการส่งคืนของฟังก์ชันจาก NSDate เป็น NSDate หรือไม่ ซึ่งช่วยให้มันรวบรวม แต่ฉันไม่แน่ใจว่าทำไมมันต้องเป็นโมฆะ
วุฒิสมาชิก

1
@TheSenator NSCalendar init ส่งคืนตัวเลือก (?) ดังนั้น gregorian จึงเป็นตัวเลือก (?) การเข้าถึงความต้องการที่เป็นตัวเลือกในการเชื่อมโยงเพิ่มเติมเพื่อเข้าถึงดังนั้น gregorian? .dateFromCoomponents เป็นตัวเลือกการโยงที่จะแกะค่าจากตัวเลือก gregorian (NSCalendar) เพิ่มเติมสำหรับการผูกมัดตัวเลือกที่นี่
iluvatar_GR

NSGregorianCalendar ถูกเลิกใช้ตั้งแต่ iOS 8 ดังนั้นให้ใช้ NSCalendarIdentifierGregorian แทน
Adam Knights

2
@The Senator บ่น coz Swift มีโครงสร้าง Date และรหัสนี้จะประกาศวันที่อีกครั้งเป็นคลาส ดังนั้นคุณควรทำเช่น: 'ขยายวันที่' และ 'คง func'
Zaporozhchenko Oleksandr

บันทึกช่วยจำเกี่ยวกับการแยกสตริงกลับไปเป็นวันที่ สตริง "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? คิด?
Luke Zammit

@LukeZammit เพิ่มเขตเวลาเพื่อdateComponents
Adrian

ลองใช้วิธีนี้โดยไม่มีโชค - 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
Zaporozhchenko Oleksandr

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 ว่านี่ควรเป็น initializer ที่พร้อมใช้งานเมื่อคุณป้อนข้อมูลเมล็ดพันธุ์เราอาจมีที่ไม่พร้อมใช้งาน (เหมือนที่มีอยู่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 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.