如何检查一个NSDate是否在另外两个NSDate之间发生

93

我正在尝试使用NSDate确定当前日期是否在日期范围内。

例如,您可以使用NSDate获取当前日期/时间:

NSDate rightNow = [NSDate date];

我希望使用那个日期来检查它是否在上午9点至下午5点的范围内。


1
您可以查看此问题的答案(http://stackoverflow.com/questions/947947/how-to-determine-if-an-nstimeinterval-occurred-during-an-arbitrary-nsdate)以获取有关如何创建特定时间段(例如下午5点至晚上9点)的NSDate对象的更多信息。 - Marc Charbonneau
10个回答

173

我想出了一个解决方案。如果您有更好的解决方案,请随意留下来,我会将其标记为正确答案。

+ (BOOL)date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate
{
    if ([date compare:beginDate] == NSOrderedAscending)
        return NO;

    if ([date compare:endDate] == NSOrderedDescending) 
        return NO;

    return YES;
}

3
看起来不错,但你可能想将其重新命名为类似这样的内容:+(BOOL)日期:(NSDate *)date是否在开始日期和结束日期之间以避免混淆。 - Jeff Kelley
3
代码高尔夫!编译器应该短路比较,使效率大致相等:return (([date compare:beginDate] != NSOrderedDescending) && ([date compare:endDate] != NSOrderedAscending)); (说明:该代码要求编译器在比较时进行短路处理,以实现与原代码大致相同的效率。) - Tim
1
更好的做法是,我会将其作为实例方法(添加到NSDate)而不是类方法。我会使用这个:-isBetweenDate:andDate: - Dave Batton
4
太好了!我根据这个创建了一个类别:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate; - Matteo Alessani
2
不错的解决方案,你甚至可以通过短路和基本的德摩根定理应用来使它更简洁。return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending); - Gabriele Petronella
显示剩余6条评论

13

首先,使用@kperryua的答案构建你想要比较的NSDate对象。从你对自己问题的回答来看,你好像已经想到了这一点。

对于实际比较日期,我完全同意@Tim在你的问题上的评论。它更加简洁,但实际上与你的代码完全相同,我将解释为什么。

+ (BOOL) date:(NSDate*)date isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate {
    return (([date compare:beginDate] != NSOrderedAscending) && ([date compare:endDate] != NSOrderedDescending));
}

虽然看起来返回语句必须评估&&操作符的两个操作数,但实际上并非如此。关键在于"短路求值",这在各种编程语言中都有实现,当然也包括C语言。基本上,如果第一个参数为0(或NO、nil等),运算符&&&会"短路",而如果第一个参数不是0,则|||也会这样做。如果datebeginDate之前,则测试返回NO,甚至不需要与endDate进行比较。基本上,它执行与您的代码相同的操作,但在一行上以单个语句的形式执行,而不是5行(或7行,包括空格)。
这旨在提供建设性的意见,因为当程序员了解他们特定的编程语言如何评估逻辑表达式时,他们可以更有效地构造它们,而不需要过多考虑效率。然而,有类似的测试可能不太有效,因为并非所有运算符都能够短路。(实际上,大多数运算符都不能短路,如数值比较运算符。)如果有疑问,在拆分逻辑时明确是始终安全的,但当您让语言/编译器为您处理小事情时,代码可以更易读。

1
明白了。我完全不知道Objective-C会进行短路评估。既然你说它会这样做,我就相信你的话。谢谢你为我解惑。我想我会把你的答案标记为正确,因为它比我的回答更简洁...而且我也学到了东西 :) - Brock Woolf
不要只听我的,我提倡“信任但验证”的原则(感谢罗纳德·里根!),这样只是为了满足我的好奇心并学习什么时候某些东西起作用和什么时候不起作用的界限。您可以通过对两个记录调用时返回值的方法进行&&运算来验证它;您会注意到,如果第一个方法返回0,则永远不会调用第二个方法。 - Quinn Taylor

7

用Swift编写的Brock Woolf版本:

extension NSDate
{
    func isBetweenDates(beginDate: NSDate, endDate: NSDate) -> Bool
    {
        if self.compare(beginDate) == .OrderedAscending
        {
            return false
        }

        if self.compare(endDate) == .OrderedDescending
        {
            return false
        }

        return true
    }
}

4

如果您的目标是 iOS 10.0+/macOS 10.12+,那么可以使用 DateInterval 类。

首先,使用起始日期和结束日期创建一个日期区间:

let start: Date = Date()
let middle: Date = Date()
let end: Date = Date()

let dateInterval: DateInterval = DateInterval(start: start, end: end)

接下来,使用 DateIntervalcontains 方法检查日期是否在间隔内:

let dateIsInInterval: Bool = dateInterval.contains(middle) // true

4

继续Quinn和Brock的解决方案,很好地子类化NSDate实现,以便可以在任何地方像这样使用:

-(BOOL) isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate {
    return (([self compare:beginDate] != NSOrderedAscending) && ([self compare:endDate] != NSOrderedDescending));
}

在你的代码的任何部分,你都可以这样使用它:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate,thisNSDate和thatNSDate当然都是NSDates :)

4

可以使用日期时间间隔轻松实现此操作,例如:

const NSTimeInterval i = [date timeIntervalSinceReferenceDate];
return ([startDate timeIntervalSinceReferenceDate] <= i &&
        [endDate timeIntervalSinceReferenceDate] >= i);

4
如果您想知道当前日期是否在给定时间段内(即2009年7月1日上午9点至下午5点),请使用NSCalendar和NSDateComponents构建所需时间的NSDate实例,并将其与当前日期进行比较。
如果您想知道当前日期是否在每天这两个小时之间,那么您可能需要采取另一种方法。使用NSCalendar和NSDate创建NSDateComponents对象,并比较小时组件。

3

针对这个问题,有更好、更快捷的解决方案。

extention Date {
    func isBetween(from startDate: Date,to endDate: Date) -> Bool {
        let result = (min(startDate, endDate) ... max(startDate, endDate)).contains(self)
        return result
    }
}

然后您可以这样调用它。
todayDate.isBetween(from: startDate, to: endDate)

即使您随机传递日期,该扩展程序也会检查哪个日期最小,哪个日期不是。

您可以在Swift 3及以上版本中使用它。


2

使用Swift 5,您可以使用以下两种解决方案之一来检查日期是否在另外两个日期之间。


#1. 使用DateIntervalcontains(_:)方法

DateInterval有一个名为contains(_:)的方法。 contains(_:)具有以下声明:

func contains(_ date: Date) -> Bool

指示此时间间隔是否包含给定日期。

以下Playground代码展示了如何使用contains(_:)来检查一个日期是否在另外两个日期之间:

import Foundation

let calendar = Calendar.current
let startDate = calendar.date(from: DateComponents(year: 2010, month: 11, day: 22))!
let endDate = calendar.date(from: DateComponents(year: 2015, month: 5, day: 1))!
let myDate = calendar.date(from: DateComponents(year: 2012, month: 8, day: 15))!

let dateInterval = DateInterval(start: startDate, end: endDate)
let result = dateInterval.contains(myDate)
print(result) // prints: true

#2. 使用 ClosedRangecontains(_:) 方法

ClosedRange 有一个叫做 contains(_:) 的方法。 contains(_:) 的声明如下:

func contains(_ element: Bound) -> Bool

返回一个布尔值,指示给定元素是否包含在范围内。

以下Playground代码展示了如何使用contains(_:)来检查一个日期是否在另外两个日期之间:

import Foundation

let calendar = Calendar.current
let startDate = calendar.date(from: DateComponents(year: 2010, month: 11, day: 22))!
let endDate = calendar.date(from: DateComponents(year: 2015, month: 5, day: 1))!
let myDate = calendar.date(from: DateComponents(year: 2012, month: 8, day: 15))!

let range = startDate ... endDate
let result = range.contains(myDate)
//let result = range ~= myDate // also works
print(result) // prints: true

-2
一个更好的 Swift 版本:
@objc public class DateRange: NSObject {
    let startDate: Date
    let endDate: Date

    init(startDate: Date, endDate: Date) {
        self.startDate = startDate
        self.endDate = endDate
    }

    @objc(containsDate:)
    func contains(_ date: Date) -> Bool {
        let startDateOrder = date.compare(startDate)
        let endDateOrder = date.compare(endDate)
        let validStartDate = startDateOrder == .orderedAscending || startDateOrder == .orderedSame
        let validEndDate = endDateOrder == .orderedDescending || endDateOrder == .orderedSame
        return validStartDate && validEndDate
    }
}

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