วิธีตรวจสอบว่า NSDate เกิดขึ้นระหว่าง NSDates อื่นสองตัวหรือไม่


91

ฉันกำลังพยายามหาว่าวันที่ปัจจุบันอยู่ในช่วงวันที่โดยใช้ NSDate หรือไม่

ตัวอย่างเช่นคุณสามารถรับวันที่ / เวลาปัจจุบันโดยใช้ NSDate:

NSDate rightNow = [NSDate date];

จากนั้นฉันต้องการใช้วันที่ดังกล่าวเพื่อตรวจสอบว่าอยู่ในช่วง9.00-17.00 น . หรือไม่


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 *) วันที่ isBetweenDate: (NSDate *) beginDate andDate: (NSDate *) endDate เพื่อหลีกเลี่ยงความสับสน
Jeff Kelley

3
โค้ดกอล์ฟ! คอมไพเลอร์ควรลัดวงจรการเปรียบเทียบทำให้ประสิทธิภาพโดยประมาณเท่ากับสิ่งที่คุณมี: return (([date Compare: beginDate]! = NSOrderDescending) && ([date Compare: endDate]! = NSOrderAscending));
ทิม

1
ยังดีกว่าฉันจะทำให้เป็นวิธีการอินสแตนซ์ (นอกเหนือจาก NSDate) ไม่ใช่วิธีคลาส ฉันจะไปกับสิ่งนี้: -isBetweenDate: andDate:
Dave Batton

4
ยอดเยี่ยม! ฉันสร้างหมวดหมู่จากสิ่งนี้:- (BOOL)isBetweenDate:(NSDate*)beginDate andDate:(NSDate*)endDate;
Matteo Alessani

2
ทางออกที่ดีคุณสามารถทำให้มันละเอียดน้อยลงได้ด้วยการลัดวงจร (และแอปพลิเคชัน De Morgan พื้นฐานreturn !([date compare:startDate] == NSOrderedAscending || [date compare:endDate] == NSOrderedDescending);
Gabriele Petronella

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, ศูนย์ ฯลฯ ) ในขณะที่|และ||ทำเช่นเดียวกันถ้าอาร์กิวเมนต์แรกคือไม่ 0 ถ้าdateมาก่อนbeginDateผลตอบแทนการทดสอบโดยไม่ต้องไปเปรียบเทียบกับNO endDateโดยทั่วไปจะทำสิ่งเดียวกันกับรหัสของคุณ แต่เป็นคำสั่งเดียวในบรรทัดเดียวไม่ใช่ 5 (หรือ 7 ที่มีช่องว่าง)

สิ่งนี้มีจุดมุ่งหมายเพื่อเป็นอินพุตที่สร้างสรรค์เนื่องจากเมื่อโปรแกรมเมอร์เข้าใจวิธีที่ภาษาโปรแกรมเฉพาะของตนประเมินนิพจน์เชิงตรรกะพวกเขาสามารถสร้างได้อย่างมีประสิทธิภาพมากขึ้นโดยไม่ต้องมีประสิทธิภาพมากนัก อย่างไรก็ตามมีการทดสอบที่คล้ายกันซึ่งจะมีประสิทธิภาพน้อยกว่าเนื่องจากตัวดำเนินการบางรายไม่ลัดวงจร (อันที่จริงแล้วส่วนใหญ่ไม่สามารถลัดวงจรได้เช่นตัวดำเนินการเปรียบเทียบเชิงตัวเลข) เมื่อมีข้อสงสัยการแยกตรรกะของคุณออกจากกันอย่างชัดเจนจะปลอดภัยเสมอ แต่โค้ดจะอ่านได้ง่ายขึ้นมากเมื่อคุณปล่อยให้ภาษา / คอมไพเลอร์จัดการกับสิ่งเล็กน้อย สำหรับคุณ.


1
เข้าใจได้. ฉันไม่รู้เลยว่า Objective-C จะทำการประเมินการลัดวงจร ฉันจะใช้คำของคุณว่ามันเป็นอย่างไร ขอบคุณที่แจ้งให้ฉันทราบ ฉันคิดว่าฉันจะทำเครื่องหมายว่าคุณถูกต้องเนื่องจากคำตอบของคุณกระชับกว่าของฉัน ... และฉันได้เรียนรู้บางอย่าง :)
Brock Woolf

ไม่จำเป็นต้องใช้คำพูดของฉัน - ฉันสนับสนุนหลักการ "เชื่อมั่น แต่ยืนยัน" (ขอบคุณโรนัลด์เรแกน!) หากเพียงเพื่อตอบสนองความอยากรู้อยากเห็นของฉันและเรียนรู้ขอบเขตของเวลาที่บางสิ่งได้ผลและไม่ได้ผล คุณสามารถตรวจสอบได้โดยทำ && สองวิธีที่บันทึกสิ่งที่ส่งคืนเมื่อถูกเรียก คุณจะสังเกตเห็นว่าถ้าอันแรกคืนค่า 0 อันที่สองจะไม่ถูกเรียก
Quinn Taylor

7

เวอร์ชัน Brock Woolf ใน 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 น. ของวันที่ 7/1/52) ให้ใช้ 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 และ 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)

แม้ว่าคุณจะผ่านการสุ่มวันที่ได้เนื่องจากส่วนขยายนี้จะตรวจสอบว่าส่วนขยายใดต่ำสุดและส่วนขยายใดไม่ได้

คุณสามารถใช้ใน swift 3 ขึ้นไป


2

ด้วย Swift 5 คุณสามารถใช้หนึ่งในสองวิธีด้านล่างเพื่อตรวจสอบว่าวันที่เกิดขึ้นระหว่างวันที่สองวันอื่น ๆ หรือไม่


# 1. ใช้DateInterval's contains(_:)วิธี

DateIntervalcontains(_:)มีวิธีการที่เรียกว่า 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. ใช้ClosedRange's contains(_:)วิธี

ClosedRangecontains(_:)มีวิธีการที่เรียกว่า 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

-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
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.