检查日期是否在两个日期之间。

45

我有一段代码,将一个字符串转换为日期对象

let date2 = KeysData[indexPath.row]["starttime"] as? String

let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"

if let date = dateFormatter.dateFromString(date2!) {
   println(date)          
}

我想知道当前日期是否在数组 startdateendate 中所给出的两个日期之间。

8个回答

170

Swift ≧ 3

Swift 3使这变得更加容易。

let fallsBetween = (startDate ... endDate).contains(Date())

现在NSDate已经转换为值类型Date并符合Comparable,我们可以创建一个ClosedRange<Date>并使用contains方法来查看当前日期是否包含在内。

注意:endDate必须大于或等于startDate。否则将无法形成范围,代码将崩溃并显示fatalError

这是安全的:

extension Date {
    func isBetween(_ date1: Date, and date2: Date) -> Bool {
        return (min(date1, date2) ... max(date1, date2)).contains(self)
    }
}

3
这可能值得您编辑之前接受的答案。这太好了! - Chris Wagner
1
@XcodeNOOB:对我来说可行。Swift的ClosedRangecontains语义意味着开始和结束日期都是包含在内的:(startDate...endDate).contains(startDate) == true(startDate...endDate).contains(endDate) == true - Nikolai Ruhe
6
警告:如果开始日期晚于结束日期,该应用程序将崩溃。除非您可以100%确定永远不会发生这种情况,否则建议添加 guard startDate < endDate else {...} - jchmilew
1
@denis631 Swift的Range类型由下限和上限组成,中间不包含任何元素。这与Python的range()函数不同,后者会构建一个值数组。当边界类型为Date时,甚至无法(或至少不明智)创建中间值:应该使用哪种分辨率(天?秒?纳秒?)。上述日期范围不是CountableRange,因此无法迭代。 - Nikolai Ruhe
11
date1...date2 ~= self 的意思是 "日期范围 date1 到 date2 是否包含了日期 self"。 - Roman Filippov
显示剩余10条评论

60

Swift 2

有更好的答案请参见Swift ≧ 3

您已经拥有将您的日期字符串转换为NSDate的代码。startdateenddate是您要检查的两个日期,您只需要检查当前日期是否在这两个日期之间:

let startDate = ...
let endDate = ...

NSDate().isBetween(date: startDate, andDate: endDate)

extension NSDate {
    func isBetweeen(date date1: NSDate, andDate date2: NSDate) -> Bool {
        return date1.compare(self) == self.compare(date2)
    }
}

编辑:如果您想执行包括范围检查,请使用此条件:

 extension NSDate {
    func isBetween(date date1: NSDate, andDate date2: NSDate) -> Bool {
        return date1.compare(self).rawValue * self.compare(date2).rawValue >= 0
    }
}

3
这是一个不错的“技巧”,但或许应该补充说明,这会检查“严格之间”,即开始和结束日期不包括在内(这可能是想要的,也可能不是想要的)。 - Martin R
以这种方式依赖于NSComparisonResult的基础值可能并不是最安全的做法。它是一个Int值,因为该枚举被Objective-C使用,而Objective-C只允许整数类型的枚举。将原始值相乘假定了底层整数的值,我认为这并不能得到保证。 - Abizern
@Abizern 我同意用这种方式使用原始值有点费解。另一方面:这些值怎么可能不是确切的-10+1?文档和标头明确定义了这些整数值。如果NSComparisonResult的值在某个时候发生变化,很多代码都会崩溃。 - Nikolai Ruhe
1
@Abizern 我同意第二个代码示例中包含检查的写法过于晦涩难懂。 - Nikolai Ruhe
@NikolaiRuhe 我对更新后的答案没什么问题 :P - Abizern
显示剩余2条评论

9

对于Swift 4.2+,我使用了基于上面答案的这个扩展:

extension Date {
    func isBetween(_ date1: Date, and date2: Date) -> Bool {
        return (min(date1, date2) ... max(date1, date2)) ~= self
    }
}

但是要小心。如果这个扩展程序没有包括您的开始日期(date1),那么请检查您日期的时间。也许你需要从日期中截取时间来修复它。例如,像这样:

let myDateWithoutTime = Calendar.current.startOfDay(for: myDate)

4

DateInterval有一个.contains函数可以实现此功能(请参阅文档):

extension Date {
    func isBetween(_ date1: Date, _ date2: Date) -> Bool {
        date1 < date2
            ? DateInterval(start: date1, end: date2).contains(self)
            : DateInterval(start: date2, end: date1).contains(self)
    }
}

然后您可以像这样使用它:
let date = Date()
let date1 = Date(timeIntervalSinceNow: 1000)
let date2 = Date(timeIntervalSinceNow: -1000)
date.isBetween(date1, date2) // true

请注意,如果需要包括结束日期,则应该这样做,或者添加一个guard来捕获它:
guard self != max(date1, date2) || self == min(date1, date2) else { return false }

经过一些重构,你最终可能会得到类似这样的代码:
extension Date {
    func isBetween(_ date1: Date, _ date2: Date) -> Bool {
        let minDate = min(date1, date2)
        let maxDate = max(date1, date2)

        guard self != minDate else { return true }
        guard self != maxDate else { return false }

        return DateInterval(start: minDate, end: maxDate).contains(self)
    }
}

那么这将通过以下测试用例:
XCTAssert(
    try XCTUnwrap(Date(fromString: "2020/01/15 09:30")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 09:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00"))
    )
)

XCTAssert(
    try XCTUnwrap(Date(fromString: "2020/01/16 01:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 23:00")),
        try XCTUnwrap(Date(fromString: "2020/01/16 04:00"))
    )
)

XCTAssert(
    try XCTUnwrap(Date(fromString: "2020/01/15 10:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 10:30"))
    )
)

XCTAssert(
    try XCTUnwrap(Date(fromString: "2020/01/15 10:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00"))
    )
)

XCTAssert(
    try XCTUnwrap(Date(fromString: "2020/01/15 09:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 08:00"))
    )
)

XCTAssert(
    try XCTUnwrap(Date(fromString: "2020/01/15 08:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 08:00"))
    )
)

XCTAssertFalse(
    try XCTUnwrap(Date(fromString: "2020/01/15 09:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 13:00"))
    )
)

XCTAssertFalse(
    try XCTUnwrap(Date(fromString: "2020/01/15 10:30")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 10:30"))
    )
)

XCTAssertFalse(
    try XCTUnwrap(Date(fromString: "2020/01/15 10:00")).isBetween(
        try XCTUnwrap(Date(fromString: "2020/01/15 10:00")),
        try XCTUnwrap(Date(fromString: "2020/01/15 08:00"))
    )
)

let date = Date()
let date1 = Date(timeIntervalSinceNow: 1000)
let date2 = Date(timeIntervalSinceNow: -1000)
XCTAssert(date.isBetween(date1, date2))

2
extension Date
{
    func isBetween(startDate:Date, endDate:Date)->Bool
    {
         return (startDate.compare(self) == .orderedAscending) && (endDate.compare(self) == .orderedDescending)
    }
}

2

这里没有明确涵盖起始和结束之间可能更精确的边界测试的答案。

例如,从0900开始到1000结束的事件应该有返回以下结果的情况:

08:59:59    false
09:00:00    true
09:59:59    true
10:00:00    false

如果您希望达到这个目标,那么以下扩展程序适合您使用:
extension Date {

    func isBetween(_ startDate: Date, and endDate: Date) -> Bool {
        return startDate <= self && self < endDate
    }

}

1

以下是来自Roman的评论的简洁易懂的Swift版本:

extension Date {
    func isBetween(_ start: Date, _ end: Date) -> Bool {
        start...end ~= self
    }
}

然后使用它:
if Date().isBetween(someDate, someOtherDate) {
    // Current date is between those dates
}

-1
extension Date {

    func isBetweeen(date date1: Date, andDate date2: Date) -> Bool {
        return date1.timeIntervalSince1970 < self.timeIntervalSince1970 && date2.timeIntervalSince1970 > self.timeIntervalSince1970
    }

}

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