1
votes

I'm stuck in a problem with my Core Data model and a fetch request that involves dates.

I have some objects in a entity with a NSDate attribute; I need to extract the objects with the date of today but I always get nil from this code:

public func getObjectsOfToday() -> Array<myObject>?
{
    let entityDescription = NSEntityDescription.entityForName("Objects", inManagedObjectContext: DataAccess.sharedInstance.managedObjectContext)

    let request = NSFetchRequest()
    request.entity = entityDescription
    request.returnsObjectsAsFaults = false

    let today = NSDate()
    request.predicate = NSPredicate(format: "(dateStart => %@) AND (dateStart <= %@)", today, today)

    var objects: [AnyObject]?
    do
    {
        objects = try DataAccess.sharedInstance.managedObjectContext.executeFetchRequest(request)
    }
    catch let error as NSError
    {
        print(error)
        objects = nil
    }

    return objects as? Array<Objects>
}

the problem I think it's the NSPredicate because it considers also hours, minute and seconds. If I print today is something like:

Printing description of today: 2016-02-28 22:02:01 +0000

but I want to fetch objects with just the same date, ignoring hours, minutes and seconds. What I need to do?

I also tried to create another NSDate using components:

let components = cal.components([.Day , .Month, .Year ], fromDate: today)
let newDate = cal.dateFromComponents(components)

but the result it's the same. What am I doing wrong?

2

2 Answers

5
votes

What I do is compare it to the start and end of the day and have a couple helper functions to calculate them:

class DateHelper{
    internal class func startOfDay(day: NSDate) -> NSDate {
        let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
        let unitFlags: NSCalendarUnit = [.Minute, .Hour, .Day, .Month, .Year]
        let todayComponents = gregorian!.components(unitFlags, fromDate: day)
        todayComponents.hour = 0
        todayComponents.minute = 0
        return (gregorian?.dateFromComponents(todayComponents))!
    }

    internal class func endOfDay(day: NSDate) -> NSDate {
        let gregorian = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
        let unitFlags: NSCalendarUnit = [.Minute, .Hour, .Day, .Month, .Year]
        let todayComponents = gregorian!.components(unitFlags, fromDate: day)
        todayComponents.hour = 23
        todayComponents.minute = 59
        return (gregorian?.dateFromComponents(todayComponents))!
    }
}

So in your code, you would call:

request.predicate = NSPredicate(format: "(dateStart => %@) AND (dateStart <= %@)", DateHelper.startOfDay(today), DateHelper.endOfDay(today))
1
votes

create a start date, get the length of the day (interval), add the interval to start date to get the next day's start.

var startOfToday: NSDate?
var interval: NSTimeInterval = 0

NSCalendar.currentCalendar().rangeOfUnit(.Day, startDate: &startOfToday, interval: &interval, forDate: NSDate())
let startOfTomorrow = startOfToday!.dateByAddingTimeInterval(interval)

create the predicate

let predicate = NSPredicate(format: "dateStart >= %@ AND dateStart < %@", startOfToday, startOfTomorrow)

I used the following test code without the core data hassle

import Foundation

let dates:[NSDate] = {
    var dates:[NSDate] = []

    dates.append({
        let c = NSDateComponents()
        c.year = 2016
        c.month = 2
        c.day = 1
        return NSCalendar.currentCalendar().dateFromComponents(c)!
    }())

    dates.append({
        let c = NSDateComponents()
        c.year = 2016
        c.month = 2
        c.day = 3
        return NSCalendar.currentCalendar().dateFromComponents(c)!
        }())

    dates.append({
        let c = NSDateComponents()
        c.year = 2016
        c.month = 3
        c.day = 1
        return NSCalendar.currentCalendar().dateFromComponents(c)!
        }())
    dates.append({
        let c = NSDateComponents()
        c.year = 2016
        c.month = 2
        c.day = 28
        c.hour = 12
        c.minute = 30
        return NSCalendar.currentCalendar().dateFromComponents(c)!
        }())
    dates.append({
        let c = NSDateComponents()
        c.year = 2016
        c.month = 2
        c.day = 28
        c.hour = 11
        c.minute = 15
        return NSCalendar.currentCalendar().dateFromComponents(c)!
        }())
    return dates
}()


var startOfToday: NSDate?
var interval: NSTimeInterval = 0

NSCalendar.currentCalendar().rangeOfUnit(.Day, startDate: &startOfToday, interval: &interval, forDate: NSDate())
if let startOfToday = startOfToday  {

    let startOfTomorrow = startOfToday.dateByAddingTimeInterval(interval)

    let predicate = NSPredicate(format: "self >= %@ AND self < %@", startOfToday, startOfTomorrow)

    let filteredArray = dates.filter({predicate.evaluateWithObject($0)})
    print(filteredArray)
}

Result:

[2016-02-28 11:30:00 +0000, 2016-02-28 10:15:00 +0000]