如何在Swift中仅比较日期的时间?

19

我有两个日期对象:

  1. 2017-01-13 11:40:17 +0000

  2. 2016-03-15 10:22:14 +0000

我需要比较这些值的时间并忽略日期。

例如:12:00am 和 12:01am,12:01 后面,所以(12:01am > 12:00am)== true。


"Compare"是什么意思? - matt
查看哪个更大或更小,附加示例 - Dylan
它们是日期对象还是字符串? - Fay007
它们是日期对象。 - Dylan
8个回答

21
最终我选择了这条路线,可以轻松比较 Swift 中的日期时间

新时间对象:

class Time: Comparable, Equatable {
init(_ date: Date) {
    //get the current calender
    let calendar = Calendar.current

    //get just the minute and the hour of the day passed to it
    let dateComponents = calendar.dateComponents([.hour, .minute], from: date)

        //calculate the seconds since the beggining of the day for comparisions
        let dateSeconds = dateComponents.hour! * 3600 + dateComponents.minute! * 60

        //set the varibles
        secondsSinceBeginningOfDay = dateSeconds
        hour = dateComponents.hour!
        minute = dateComponents.minute!
    }

    init(_ hour: Int, _ minute: Int) {
        //calculate the seconds since the beggining of the day for comparisions
        let dateSeconds = hour * 3600 + minute * 60

        //set the varibles
        secondsSinceBeginningOfDay = dateSeconds
        self.hour = hour
        self.minute = minute
    }

    var hour : Int
    var minute: Int

    var date: Date {
        //get the current calender
        let calendar = Calendar.current

        //create a new date components.
        var dateComponents = DateComponents()

        dateComponents.hour = hour
        dateComponents.minute = minute

        return calendar.date(byAdding: dateComponents, to: Date())!
    }

    /// the number or seconds since the beggining of the day, this is used for comparisions
    private let secondsSinceBeginningOfDay: Int

    //comparisions so you can compare times
    static func == (lhs: Time, rhs: Time) -> Bool {
        return lhs.secondsSinceBeginningOfDay == rhs.secondsSinceBeginningOfDay
    }

    static func < (lhs: Time, rhs: Time) -> Bool {
        return lhs.secondsSinceBeginningOfDay < rhs.secondsSinceBeginningOfDay
    }

    static func <= (lhs: Time, rhs: Time) -> Bool {
        return lhs.secondsSinceBeginningOfDay <= rhs.secondsSinceBeginningOfDay
    }


    static func >= (lhs: Time, rhs: Time) -> Bool {
        return lhs.secondsSinceBeginningOfDay >= rhs.secondsSinceBeginningOfDay
    }


    static func > (lhs: Time, rhs: Time) -> Bool {
        return lhs.secondsSinceBeginningOfDay > rhs.secondsSinceBeginningOfDay
    }
}

日期扩展,便于访问: //添加了仅从日期获取时间的功能:

extension Date {
    var time: Time {
        return Time(self)
    }
}

例子:

let firstDate = Date()
let secondDate = firstDate

//Will return true
let timeEqual = firstDate.time == secondDate.time

我太懒了,没实现这个。谢谢 :') - Arda Oğul Üçpınar

15
远比被接受的答案简单: SWIFT 4
// date1 and date2 are the dates you want to compare

let calendar = Calendar.current

var newDate = Date(TimeIntervalSinceReferenceDate: 0) // Initiates date at 2001-01-01 00:00:00 +0000
var newDate1 = Date(TimeIntervalSinceReferenceDate: 0) // Same as above

// Recieving the components from the dates you want to compare 
let newDateComponents = calendar.dateComponents([.hour, .minute], from: date1)!
let newDate1Components = calendar.dateComponents([.hour, .minute], from: date2)!

// Adding those components
newDate = calendar.date(byAdding: newDateComponents, to: newDate)
newDate1 = calendar.date(byAdding: newDate1Components, to: newDate1)

9

我的做法是使用Calendar将它们转换为相同日期的Date对象,然后使用例如timeIntervalSinceReferenceDate进行比较。

另一种更简洁的方法(但很可能会产生更多的代码行)是为Date创建一个扩展名叫做secondsFromBeginningOfTheDay() -> TimeInterval,然后比较结果的double值。

基于第二种方法的示例:

// Creating Date from String
let textDate1 = "2017-01-13T12:21:00-0800"
let textDate2 = "2016-03-06T20:12:05-0900"

let dateFormatter: DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZZZ"
    formatter.timeZone = TimeZone.current
    return formatter
} ()

// Dates used for the comparison
let date1 = dateFormatter.date(from: textDate1)
let date2 = dateFormatter.date(from: textDate2)




// Date extensions
extension Date {
    func secondsFromBeginningOfTheDay() -> TimeInterval {
        let calendar = Calendar.current
        // omitting fractions of seconds for simplicity
        let dateComponents = calendar.dateComponents([.hour, .minute, .second], from: self)

        let dateSeconds = dateComponents.hour! * 3600 + dateComponents.minute! * 60 + dateComponents.second!

        return TimeInterval(dateSeconds)
    }

    // Interval between two times of the day in seconds
    func timeOfDayInterval(toDate date: Date) -> TimeInterval {
        let date1Seconds = self.secondsFromBeginningOfTheDay()
        let date2Seconds = date.secondsFromBeginningOfTheDay()
        return date2Seconds - date1Seconds
    }
}

if let date1 = date1, let date2 = date2 {
    let diff = date1.timeOfDayInterval(toDate: date2)

    // as text
    if diff > 0 {
        print("Time of the day in the second date is greater")
    } else if diff < 0 {
        print("Time of the day in the first date is greater")
    } else {
        print("Times of the day in both dates are equal")
    }


    // show interval as as H M S
    let timeIntervalFormatter = DateComponentsFormatter()
    timeIntervalFormatter.unitsStyle = .abbreviated
    timeIntervalFormatter.allowedUnits = [.hour, .minute, .second]
    print("Difference between times since midnight is", timeIntervalFormatter.string(from: diff) ?? "n/a")

}

// Output: 
// Time of the day in the second date is greater
// Difference between times since midnight is 8h 51m 5s

8

我提供的解决方案是比较两个时间,而忽略日期:

let date1 = some time as a date
let date2 = some other time as a date

let time1 = 60*Calendar.current.component(.hour, from: date1!) + Calendar.current.component(.minute, from: date1!)
let time2 =  60*Calendar.current.component(.hour, from: date2!) + Calendar.current.component(.minute, from: date2!)

现在您可以比较整数time1和time2,而不需要考虑日期。如果您需要更高的精度,可以将秒数除以60。

7
这段代码可以运行,在playground轻松检查。
let s1 = "22:31"
let s2 = "14:31"
let f = DateFormatter()
f.dateFormat = "HH:mm"

f.date(from: s1)! //"Jan 1, 2000 at 10:31 PM"
f.date(from: s2)! //"Jan 1, 2000 at 2:31 PM"
f.date(from: s1)! > f.date(from: s2)!  // true

请提供文本以进行翻译。 - Hardik Kardani
我个人会这样做。对我来说,这是最简洁的方式,而且由于我有一个全局的日期格式化工具,所以不需要每次都创建一个新的。 - Jevon718

3

时间类型没有标准类型。一个合理的起点是使用元组:

typealias TimeOfDay = (hour: Int, minute: Int, second: Int)

要创建这些TimeOfDay值,您需要一个Calendar。默认情况下,Calendar使用设备的系统范围内的时区。如果您不想要这个,请明确设置Calendar的时区。例如:
var calendar = Calendar.autoupdatingCurrent
calendar.timeZone = TimeZone(abbreviation: "UTC")!

现在你可以使用 DateFormatter 将字符串转换为 Date(如果需要),然后使用 calendarDate 中提取时间组件:
let strings: [String] = ["2017-01-13 11:40:17 +0000", "2016-03-15 10:22:14 +0000"]
let parser = DateFormatter()
parser.dateFormat = "yyyy-MM-dd HH:mm:ss Z"
let timesOfDay: [TimeOfDay] = strings.map({ (string) -> TimeOfDay in
    let components = calendar.dateComponents([.hour, .minute, .second], from: parser.date(from: string)!)
    return (hour: components.hour!, minute: components.minute!, second: components.second!)
})

Swift.print(timesOfDay)
// Output: [(11, 40, 17), (10, 22, 14)]

最后,您可以比较这些TimeOfDay值。Swift带有用于元组的标准比较运算符,其元素为Comparable,因此此TimeOfDay类型符合条件。您只需要这样说:

if timesOfDay[0] < timesOfDay[1] {
    Swift.print("date[0] comes first")
} else if timesOfDay[0] == timesOfDay[1] {
    Swift.print("times are equal")
} else {
    Swift.print("date[1] comes first")
}

这会考虑到上午/下午的跨越吗?例如,下午1:00比上午11:00晚吗? - Dylan
是的,因为下午一点在元组的hour元素中表示为13。 - rob mayoff
请注意,您问题中的字符串日期没有提到上午/下午,因此我假设它们使用24小时制。 - rob mayoff

3

假设我们有两个以字符串格式表示的日期:

// "2017-01-13 11:40:17 +0000"
// "2016-03-15 10:22:14 +0000"

我们需要将这些字符串转换为日期格式,我们创建DateFormatter()并设置格式("yyyy-MM-dd' 'HH:mm:ssZ")即可完成转换。
//date formatter converts string to date in our case
let firstDateFormatter = DateFormatter()
firstDateFormatter.dateFormat = "yyyy-MM-dd' 'HH:mm:ssZ"

现在我们可以将字符串日期转换为日期格式。
   //convert string to dates
    if let date1 = firstDateFormatter.date(from: "2017-01-13 09:40:17 +0000"),
        let date2 = firstDateFormatter.date(from: "2016-03-15 10:22:14 +0000") {
    

我们想要的是仅比较小时和分钟。因此将日期格式更改为"HH:mm"。
//we ve got the dates, now switch dateformat for other job
firstDateFormatter.dateFormat = "HH:mm"

现在从我们的日期中获取只包含“HH:mm”的字符串值。

   // convert date to string ( part of string we want to compare )
        let HHmmDate1 = firstDateFormatter.string(from: date1) //"17:40"
        let HHmmDate2 = firstDateFormatter.string(from: date2) //"18:22"

最后一步是从我们的“HH:mm”值中获取日期。假设我们要求DateFormatter根据时间给我们一个日期,对于我们的情况是"17:40"和"18:22"。DateFormatter会为日期设置一些值,所以我们会自动得到Jan 1, 2000作为两个日期的日期,但它会获取我们提供的时间。
   //produce "default" dates with desired HH:mm
    //default means same date, but time is different
        let HH1 = firstDateFormatter.date(from: HHmmDate1) //"Jan 1, 2000 at 5:40 PM"
        let HH2 = firstDateFormatter.date(from: HHmmDate2) //"Jan 1, 2000 at 6:22 PM"

现在我们可以轻松比较日期

 //compare
        HH1! > HH2!
}

使用Calendar对象也有许多比较日期的选项


@quicklikerabbit 完成了!如果有什么不清楚的地方,请随时提问。 - John Eugene

1
如果你使用Swifter Swift,在Swift中这非常简单。
date1.day = 1
date1.month = 1
date1.year = 2000

date2.day = 1
date2.month = 1
date2.year = 2000

现在您可以使用 >,<,== 操作符比较 date1 和 date2 的时间组件。
编辑 - 您可以通过扩展日期类自行执行此操作,例如 Swifter-Swift 可以对日期的日组件进行操作。
public var day: Int {
        get {
            return Calendar.current.component(.day, from: self)
        }
        set {
            let allowedRange = Calendar.current.range(of: .day, in: .month, for: self)!
            guard allowedRange.contains(newValue) else { return }

            let currentDay = Calendar.current.component(.day, from: self)
            let daysToAdd = newValue - currentDay
            if let date = Calendar.current.date(byAdding: .day, value: daysToAdd, to: self) {
                self = date
            }
        }
    } 

这在 Swift 中不起作用,你不能仅仅从日期对象中设置日、月和年。不过你已经朝着正确的方向前进了。 - Dylan
是的,你说得对。我正在我们的生产代码中使用这段代码...但我们正在使用swifter-swift扩展))) - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接