Як перевірити, чи виникає NSDate між двома іншими NSDates


90

Я намагаюся з’ясувати, чи потрапляє поточна дата в діапазон дат за допомогою NSDate.

Наприклад, ви можете отримати поточну дату / час за допомогою NSDate:

NSDate rightNow = [NSDate date];

Потім я хотів би використати цю дату, щоб перевірити, чи вона знаходиться в діапазоні 9:00 - 17:00 .


1
Ви можете поглянути на відповіді на це запитання, щоб отримати докладнішу інформацію про те, як створювати об’єкти NSDate для певного періоду часу, наприклад, 17:00 - 21:00.
Марк Шарбоно

Відповіді:


167

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

+ (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, щоб уникнути плутанини.
Джефф Келлі, 02

3
Код гольфу! Компілятор повинен здійснити коротке замикання порівняння, зробивши ефективність приблизно рівною вашій: return (([порівняння дати: beginDate]! = NSOrderedDescending) && ([порівняння дати: endDate]! = NSOrderedAscending));
Тім

1
А ще краще, я б зробив це методом екземпляра (доповненням до NSDate), а не методом класу. Я б пішов із цим: -isBetweenDate: andDate:
Dave

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 (або НІ, нуль тощо). , while |і ||зробіть те саме, якщо перший аргумент не дорівнює 0. Якщо dateприходить раніше beginDate, тест повертається, NOнавіть не потребуючи порівняння з endDate. По суті, він робить те саме, що і ваш код, але в одному висловлюванні в одному рядку, а не 5 (або 7, з пробілами).

Це задумано як конструктивний вхід, оскільки, коли програмісти розуміють спосіб, яким їх конкретна мова програмування оцінює логічні вирази, вони можуть будувати їх ефективніше, не стільки про ефективність. Однак існують подібні тести, які були б менш ефективними, оскільки не у всіх операторів відбувається коротке замикання. (Дійсно, більшість не можуть замикати, наприклад, числові оператори порівняння.) Якщо ви сумніваєтесь, завжди можна бути чітким, розбиваючи свою логіку, але код може бути набагато зручнішим для читання, коли ви дозволяєте мові / компілятору обробляти дрібниці для вас.


1
Зрозуміло. Я не уявляв, що Objective-C буде проводити оцінки короткого замикання. Я повірю вам на слово, що це так. Дякую, що це мені зрозуміли. Припускаю, я позначу вас як правильних, оскільки ваша відповідь є більш стислою, ніж моя власна ... і я чомусь навчився :)
Брок Вульф

Не потрібно вірити мені на слово - я відстоюю принцип "довіряй, але перевіряй" (дякую, Рональде Рейгану!), Хоча б лише для того, щоб задовольнити свою цікавість і дізнатися межі того, коли щось працює, а ні. Ви можете перевірити це, виконавши && двох методів, які реєструють те, що вони повертають при виклику; Ви помітите, що якщо перший повертає 0, другий ніколи не буде викликаний.
Quinn Taylor

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:00 - 17:00, 01.07.09), використовуйте 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

Продовжуючи рішення Quinn's та 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 :)


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)

Навіть ви можете передавати дату випадковим чином, оскільки це розширення перевіряє, яка з них є мінімальною, а яка - ні.

ви можете використовувати його швидко в 3 і вище.


2

За допомогою Swift 5 ви можете використовувати одне з двох наведених нижче рішень, щоб перевірити, чи відбувається дата між двома іншими датами.


№1. За допомогою методу DateInterval'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'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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.