Bagaimana cara memeriksa apakah NSDate terjadi di antara dua NSDate lainnya

91

Saya mencoba untuk mencari tahu apakah tanggal saat ini termasuk dalam rentang tanggal menggunakan NSDate atau tidak.

Misalnya, Anda bisa mendapatkan tanggal / waktu saat ini menggunakan NSDate:

NSDate rightNow = [NSDate date];

Saya kemudian ingin menggunakan tanggal tersebut untuk memeriksa apakah berada dalam kisaran 9AM - 5PM .

Brock Woolf
sumber
1
Anda dapat melihat jawaban atas pertanyaan ini untuk informasi lebih lanjut tentang cara membuat objek NSDate untuk periode waktu tertentu, seperti 17: 00-21: 00.
Marc Charbonneau

Jawaban:

168

Saya datang dengan solusi. Jika Anda memiliki solusi yang lebih baik, silakan tinggalkan dan saya akan menandainya sebagai benar.

+ (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;
}
Brock Woolf
sumber
3
Kelihatannya bagus, tetapi Anda mungkin ingin mengganti namanya menjadi seperti ini: + (BOOL) date: (NSDate *) date isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate untuk menghindari kebingungan.
Jeff Kelley
3
Kode golf! Kompiler harus melakukan hubungan pendek perbandingan, membuat efisiensi kira-kira sama dengan apa yang Anda miliki: return (([date bandingkan: beginDate]! = NSOrderedDescending) && ([date Compare: endDate]! = NSOrderedAscending));
Tim
1
Lebih baik lagi, saya akan menjadikan ini sebagai metode contoh (tambahan untuk NSDate), bukan metode kelas. Saya akan memilih ini: -isBetweenDate: andDate:
Dave Batton
4
Luar biasa! Saya membuat Kategori dari ini:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani
2
solusi yang bagus, Anda bahkan dapat membuatnya kurang bertele-tele dengan korsleting (dan aplikasi dasar De Morgan.return !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella
13

Untuk bagian pertama, gunakan jawaban dari @kperryua untuk membuat objek NSDate yang ingin Anda bandingkan. Dari jawaban Anda atas pertanyaan Anda sendiri, sepertinya Anda sudah mengetahuinya.

Untuk benar-benar membandingkan tanggal, saya setuju dengan komentar @Tim atas jawaban Anda. Ini lebih ringkas namun sebenarnya sama persis dengan kode Anda, dan saya akan menjelaskan alasannya.

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

Meskipun pernyataan return tampaknya harus mengevaluasi kedua operan operator &&, sebenarnya tidak demikian. Kuncinya adalah " evaluasi sirkuit pendek ", yang diimplementasikan dalam berbagai macam bahasa pemrograman, dan tentunya di C. Pada dasarnya, operator &dan &&"sirkuit pendek" jika argumen pertama adalah 0 (atau NO, nil, dll.) , sementara |dan ||melakukan hal yang sama jika argumen pertama bukan 0. Jika datemuncul sebelumnya beginDate, tes kembali NOtanpa perlu membandingkan endDate. Pada dasarnya, ini melakukan hal yang sama dengan kode Anda, tetapi dalam satu pernyataan pada satu baris, bukan 5 (atau 7, dengan spasi).

Ini dimaksudkan sebagai masukan yang konstruktif, karena ketika pemrogram memahami cara bahasa pemrograman khusus mereka mengevaluasi ekspresi logis, mereka dapat membangunnya secara lebih efektif tanpa terlalu memperhatikan efisiensi. Namun, ada tes serupa yang akan menjadi kurang efisien, karena tidak semua operator hubungan arus pendek. (Memang, sebagian besar tidak dapat mengalami hubungan arus pendek, seperti operator perbandingan numerik.) Jika ragu, selalu aman untuk menyatakan secara eksplisit dalam memecah logika Anda, tetapi kode dapat jauh lebih mudah dibaca ketika Anda membiarkan bahasa / kompilator menangani hal-hal kecil untukmu.

Quinn Taylor
sumber
1
Bisa dimengerti. Saya tidak tahu bahwa Objective-C akan melakukan evaluasi sirkuit pendek. Aku akan mempercayai kata-katamu itu. Terima kasih telah membereskannya untuk saya. Saya kira saya akan menandai Anda sebagai benar karena jawaban Anda lebih ringkas daripada jawaban saya ... dan saya belajar sesuatu :)
Brock Woolf
Tidak perlu mengambil kata-kata saya untuk itu - saya menganjurkan prinsip "percaya, tetapi verifikasi" (terima kasih, Ronald Reagan!) Jika hanya untuk memuaskan keingintahuan saya dan mempelajari batas-batas ketika sesuatu berhasil dan tidak. Anda dapat memverifikasinya dengan melakukan && dari dua metode yang mencatat apa yang mereka kembalikan saat dipanggil; Anda akan melihat bahwa jika yang pertama mengembalikan 0, yang kedua tidak akan pernah dipanggil.
Quinn Taylor
7

Versi Brock Woolf di 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
    }
}
ChikabuZ
sumber
4

Jika Anda ingin mengetahui apakah tanggal saat ini berada di antara dua titik waktu tertentu (09.00 - 17.00 pada 7/1/09), gunakan NSCalendar dan NSDateComponents untuk membuat instans NSDate untuk waktu yang diinginkan dan bandingkan dengan tanggal saat ini.

Jika Anda ingin tahu apakah tanggal saat ini berada di antara dua jam ini setiap hari, maka Anda mungkin bisa pergi ke arah lain. Buat objek NSDateComponents dengan dan NSCalendar dan NSDate Anda dan bandingkan komponen jam.

kperryua
sumber
4

Jika Anda dapat menargetkan iOS 10.0 + / macOS 10.12+, gunakan DateIntervalkelas tersebut.

Pertama, buat interval tanggal dengan tanggal mulai dan akhir:

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

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

Kemudian, periksa apakah tanggal tersebut dalam interval dengan menggunakan containsmetode DateInterval:

let dateIsInInterval: Bool = dateInterval.contains(middle) // true
Bryan Luby
sumber
3

Ini dapat dilakukan dengan mudah menggunakan interval waktu tanggal, seperti:

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

Melanjutkan solusi Quinn dan Brock, sangat bagus untuk implementasi subclass NSDate, sehingga dapat digunakan di mana saja seperti ini:

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

Dan di bagian mana pun dari kode Anda, Anda dapat menggunakannya sebagai:

[myNSDate isBetweenDate:thisNSDate andDate:thatNSDate];

(myNSDate, thisNSDate dan thatNSDate tentu saja NSDates :)

MiQUEL
sumber
3

Ada solusi yang lebih baik dan lebih cepat untuk masalah ini.

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

Kemudian Anda bisa menyebutnya seperti ini.

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

Bahkan Anda dapat melewatkan tanggal secara acak karena ekstensi ini memeriksa mana yang minimum dan mana yang tidak.

Anda bisa menggunakannya di swift 3 ke atas.

Rehan Ali
sumber
2

Dengan Swift 5, Anda dapat menggunakan salah satu dari dua solusi di bawah ini untuk memeriksa apakah tanggal terjadi di antara dua tanggal lainnya.


# 1. Menggunakan DateInterval's contains(_:)metode

DateIntervalmemiliki metode yang disebut contains(_:). contains(_:)memiliki deklarasi berikut:

func contains(_ date: Date) -> Bool

Menunjukkan apakah interval ini berisi tanggal tertentu.

Kode Playground berikut menunjukkan cara menggunakan contains(_:)untuk memeriksa apakah tanggal terjadi di antara dua tanggal lain:

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. Menggunakan ClosedRange's contains(_:)metode

ClosedRangememiliki metode yang disebut contains(_:). contains(_:)memiliki deklarasi berikut:

func contains(_ element: Bound) -> Bool

Mengembalikan nilai Boolean yang menunjukkan apakah elemen yang diberikan berada dalam rentang.

Kode Playground berikut menunjukkan cara menggunakan contains(_:)untuk memeriksa apakah tanggal terjadi di antara dua tanggal lain:

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
Imanou Petit
sumber
-1

Versi yang lebih baik di 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
sumber