Cách kiểm tra xem NSDate có xảy ra giữa hai NSDate khác không


90

Tôi đang cố gắng tìm hiểu xem ngày hiện tại có nằm trong phạm vi ngày sử dụng NSDate hay không.

Ví dụ: bạn có thể lấy ngày / giờ hiện tại bằng NSDate:

NSDate rightNow = [NSDate date];

Sau đó, tôi muốn sử dụng ngày đó để kiểm tra xem nó có nằm trong khoảng 9 giờ sáng - 5 giờ chiều hay không .


1
Bạn có thể xem các câu trả lời cho câu hỏi này để biết thêm thông tin về cách tạo các đối tượng NSDate trong một khoảng thời gian cụ thể, chẳng hạn như 5 giờ chiều - 9 giờ tối.
Marc Charbonneau

Câu trả lời:


167

Tôi đã đưa ra một giải pháp. Nếu bạn có một giải pháp tốt hơn, hãy để lại nó và tôi sẽ đánh dấu nó là đúng.

+ (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
Điều đó có vẻ ổn, nhưng bạn có thể muốn đổi tên nó như sau: + (BOOL) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate để tránh nhầm lẫn.
Jeff Kelley

3
Mã gôn! Trình biên dịch nên ngắn mạch so sánh, làm cho hiệu quả gần bằng những gì bạn có: return (([date so sánh: beginDate]! = NSOrderedDescending) && ([date so sánh: endDate]! = NSOrderedAscending));
Tim

1
Tốt hơn nữa, tôi muốn đặt nó là một phương thức instance (một bổ sung cho NSDate), không phải một phương thức lớp. Tôi sẽ đi với cái này: -isBetweenDate: andDate:
Dave Batton

4
Thông minh! Tôi đã thực hiện một loại từ này:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani

2
giải pháp tốt, bạn thậm chí có thể làm cho nó bớt dài dòng hơn bằng cách đoản mạch (và một ứng dụng De Morgan cơ bản.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella

13

Đối với phần đầu tiên, hãy sử dụng câu trả lời từ @kperryua để tạo các đối tượng NSDate mà bạn muốn so sánh với. Từ câu trả lời của bạn cho câu hỏi của riêng bạn, có vẻ như bạn đã tìm ra.

Để thực sự so sánh ngày tháng, tôi hoàn toàn đồng ý với nhận xét của @Tim về câu trả lời của bạn. Nó ngắn gọn hơn nhưng thực sự chính xác tương đương với mã của bạn và tôi sẽ giải thích tại sao.

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

Mặc dù có vẻ như câu lệnh return phải đánh giá cả hai toán hạng của toán tử &&, nhưng thực tế không phải như vậy. Chìa khóa là " đánh giá ngắn mạch ", được triển khai trong nhiều ngôn ngữ lập trình và chắc chắn là ở C. Về cơ bản, các toán tử &&&"đoản mạch" nếu đối số đầu tiên là 0 (hoặc NO, nil, v.v.) , trong khi |||làm tương tự nếu đối số đầu tiên không phải là 0. Nếu dateđến trước beginDate, kiểm tra trả về NOmà không cần so sánh với endDate. Về cơ bản, nó làm điều tương tự như mã của bạn, nhưng trong một câu lệnh duy nhất trên một dòng, không phải 5 (hoặc 7, với khoảng trắng).

Đây được coi là đầu vào mang tính xây dựng, vì khi các lập trình viên hiểu cách ngôn ngữ lập trình cụ thể của họ đánh giá các biểu thức logic, họ có thể xây dựng chúng hiệu quả hơn mà không ảnh hưởng nhiều đến hiệu quả. Tuy nhiên, có những thử nghiệm tương tự sẽ kém hiệu quả hơn, vì không phải tất cả người vận hành đều đoản mạch. (Thật vậy, hầu hết không thể đoản mạch, chẳng hạn như các toán tử so sánh số.) Khi nghi ngờ, luôn an toàn để phá vỡ logic của bạn, nhưng mã có thể dễ đọc hơn nhiều khi bạn để ngôn ngữ / trình biên dịch xử lý những việc nhỏ cho bạn.


1
Có thể hiểu được. Tôi không biết rằng Objective-C sẽ đánh giá ngắn mạch. Tôi sẽ nhận lời của bạn về nó rằng nó sẽ làm. Cảm ơn cho sạch nó lên cho tôi. Tôi cho rằng tôi sẽ đánh dấu bạn như đúng vì câu trả lời của bạn là ngắn gọn hơn của riêng tôi ... và tôi đã học một cái gì đó :)
Brock Woolf

Tôi ủng hộ nguyên tắc "tin tưởng, nhưng hãy xác minh" (cảm ơn, Ronald Reagan!) Nếu chỉ để thỏa mãn trí tò mò của tôi và tìm hiểu giới hạn của thời điểm điều gì đó hiệu quả và không. Bạn có thể xác minh nó bằng cách thực hiện && trong hai phương pháp ghi lại những gì chúng trả về khi được gọi; bạn sẽ nhận thấy rằng nếu cái đầu tiên trả về 0, cái thứ hai sẽ không bao giờ được gọi.
Quinn Taylor

7

Phiên bản Brock Woolf trong 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

Nếu bạn muốn biết liệu ngày hiện tại có rơi vào khoảng thời gian giữa hai thời điểm nhất định hay không (9 giờ sáng - 5 giờ chiều ngày 7/1/09), hãy sử dụng NSCalendar và NSDateComponents để tạo các phiên bản NSDate cho thời gian mong muốn và so sánh chúng với ngày hiện tại.

Nếu bạn muốn biết liệu ngày hiện tại có rơi vào khoảng giữa hai giờ này hàng ngày hay không, thì bạn có thể đi theo cách khác. Tạo một đối tượng NSDateComponents với và NSCalendar và NSDate của bạn và so sánh các thành phần giờ.


4

Nếu bạn có thể nhắm mục tiêu iOS 10.0 + / macOS 10.12+, thì hãy sử dụng DateIntervallớp này.

Đầu tiên, hãy tạo khoảng ngày với ngày bắt đầu và ngày kết thúc:

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

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

Sau đó, kiểm tra xem ngày có nằm trong khoảng thời gian không bằng cách sử dụng containsphương pháp DateInterval:

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

3

Điều này có thể được thực hiện dễ dàng bằng cách sử dụng các khoảng thời gian của ngày tháng, như sau:

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

3

Tiếp tục với các giải pháp của Quinn và Brock, rất tốt để triển khai NSDate lớp con, vì vậy nó có thể được sử dụng ở mọi nơi như sau:

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

Và ở bất kỳ phần nào của mã, bạn có thể sử dụng nó như:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate và thatNSDate tất nhiên là NSDates :)


3

Có giải pháp tốt hơn và thuận tiện hơn cho vấn đề này.

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

Sau đó, bạn có thể gọi nó như thế này.

todayDate.isBetween(from: startDate, to: endDate)

Thậm chí bạn có thể chuyển ngày ngẫu nhiên vì tiện ích mở rộng này kiểm tra cái nào là tối thiểu và cái nào không.

bạn có thể sử dụng nó trong phiên bản nhanh 3 trở lên.


2

Với Swift 5, bạn có thể sử dụng một trong hai giải pháp dưới đây để kiểm tra xem một ngày có xảy ra giữa hai ngày khác hay không.


# 1. Sử dụng DateInterval's contains(_:)phương pháp

DateIntervalcó một phương thức được gọi contains(_:). contains(_:)có khai báo sau:

func contains(_ date: Date) -> Bool

Cho biết khoảng thời gian này có chứa ngày nhất định hay không.

Mã Playground sau đây cho biết cách sử dụng contains(_:)để kiểm tra xem một ngày có xảy ra giữa hai ngày khác hay không:

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. Sử dụng ClosedRange's contains(_:)phương pháp

ClosedRangecó một phương thức được gọi contains(_:). contains(_:)có khai báo sau:

func contains(_ element: Bound) -> Bool

Trả về một giá trị Boolean cho biết liệu phần tử đã cho có nằm trong phạm vi hay không.

Mã Playground sau đây cho biết cách sử dụng contains(_:)để kiểm tra xem một ngày có xảy ra giữa hai ngày khác hay không:

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

Một phiên bản tốt hơn trong 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
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.