Как проверить, возникает ли NSDate между двумя другими NSDate

91

Я пытаюсь выяснить, попадает ли текущая дата в диапазон дат, используя NSDate.

Например, вы можете получить текущую дату / время с помощью NSDate:

NSDate rightNow = [NSDate date];

Затем я хотел бы использовать эту дату, чтобы проверить, находится ли она в диапазоне с 9 утра до 5 вечера .

Брок Вульф
источник
1
Вы можете взглянуть на ответы на этот вопрос, чтобы получить дополнительную информацию о том, как создавать объекты NSDate для определенного периода времени, например с 17:00 до 21:00.
Marc Charbonneau

Ответы:

168

Я придумал решение. Если у вас есть лучшее решение, не стесняйтесь оставлять его, и я отмечу его как правильное.

+ (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) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate, чтобы избежать путаницы.
Джефф Келли
3
Используйте гольф! Компилятор должен сократить сравнение, сделав эффективность примерно такой же, как у вас: return (([date compare: beginDate]! = NSOrderedDescending) && ([date compare: endDate]! = NSOrderedAscending));
Тим
1
Еще лучше, я бы сделал это методом экземпляра (дополнением к NSDate), а не методом класса. Я бы пошел с этим: -isBetweenDate: andDate:
Дэйв Баттон
4
Превосходно! Я сделал категорию из этого:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Маттео Алессани
2
хорошее решение, вы даже можете сделать его менее подробным с помощью короткого замыкания (и простого приложения Де Моргана.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Габриэль Петронелла,
13

Для первой части используйте ответ от @kperryua для создания объектов NSDate, с которыми вы хотите сравнить. Судя по вашему ответу на ваш собственный вопрос, похоже, вы уже это поняли.

Что касается фактического сравнения дат, я полностью согласен с комментарием @Tim к вашему ответу. Он более краток, но фактически эквивалентен вашему коду, и я объясню почему.

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

Хотя может показаться, что оператор return должен оценивать оба операнда оператора &&, на самом деле это не так. Ключевым моментом является « оценка короткого замыкания », которая реализована в самых разных языках программирования и, конечно, в C. В основном, операторы &и &&«короткое замыкание», если первый аргумент равен 0 (или NO, nil и т. , в то время как |и ||сделайте то же самое, если первый аргумент не 0. Если dateпредшествует beginDate, тест возвращается NOбез необходимости даже сравнивать с endDate. По сути, он делает то же самое, что и ваш код, но в одном операторе в одной строке, а не в 5 (или 7, с пробелами).

Это задумано как конструктивный ввод, поскольку, когда программисты понимают, как их конкретный язык программирования оценивает логические выражения, они могут создавать их более эффективно, не особо заботясь об эффективности. Однако существуют аналогичные тесты, которые были бы менее эффективными, поскольку не все операторы замыкают накоротко. (В самом деле, большинство из них не может замыкать короткое замыкание, например, операторы числового сравнения.) В случае сомнений всегда безопасно явным образом разбивать логику на части, но код может быть намного более читабельным, если вы позволите языку / компилятору обрабатывать мелочи. для тебя.

Куинн Тейлор
источник
1
Понятно. Я понятия не имел, что Objective-C сократит оценку. Я верю вам на слово, что это так. Спасибо, что прояснили это для меня. Я полагаю, что отмечу вас как правильного, поскольку ваш ответ более краток, чем мой собственный ... и я кое-что узнал :)
Брок Вульф,
Не нужно верить мне на слово - я защищаю принцип «доверяй, но проверяй» (спасибо, Рональд Рейган!) Хотя бы для того, чтобы удовлетворить свое любопытство и узнать границы того, когда что-то работает, а когда нет. Вы можете проверить это, выполнив && двух методов, которые регистрируют то, что они возвращают при вызове; вы заметите, что если первый возвращает 0, второй никогда не будет вызван.
Куинн Тейлор,
7

Версия Брока Вульфа в Swift:

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

Если вы хотите знать, попадает ли текущая дата между двумя заданными моментами времени (с 9 утра до 5 вечера 1 июля 2009 г.), используйте NSCalendar и NSDateComponents для создания экземпляров NSDate для желаемого времени и сравните их с текущей датой.

Если вы хотите знать, попадает ли текущая дата между этими двумя часами каждый день, вы, вероятно, можете пойти другим путем. Создайте объект NSDateComponents с NSCalendar и вашим NSDate и сравните компоненты часа.

кперрюа
источник
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)

Затем проверьте, находится ли дата в интервале, используя containsметод DateInterval:

let dateIsInInterval: Bool = dateInterval.contains(middle) // true
Брайан Луби
источник
3

Это легко сделать, используя временные интервалы дат, например:

const NSTimeInterval i = [date timeIntervalSinceReferenceDate];
return ([startDate timeIntervalSinceReferenceDate] <= i &&
        [endDate timeIntervalSinceReferenceDate] >= i);
Джастин
источник
3

Продолжая работу с решениями Куинна и Брока, очень приятно создать подкласс реализации 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 :)

MiQUEL
источник
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. Использование DateInterval«S contains(_:)метод

DateIntervalимеет метод под названием contains(_:). contains(_:)имеет следующее объявление:

func contains(_ date: Date) -> Bool

Указывает, содержит ли этот интервал указанную дату.

Следующий код игровой площадки показывает, как использовать 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. Использование ClosedRange«S contains(_:)метод

ClosedRangeимеет метод под названием contains(_:). contains(_:)имеет следующее объявление:

func contains(_ element: Bound) -> Bool

Возвращает логическое значение, указывающее, содержится ли данный элемент в диапазоне.

Следующий код игровой площадки показывает, как использовать 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
Иману Пети
источник
-1

Лучшая версия в 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
    }
}
TheCodingArt
источник