How to query a date property in Realm?

Viewed 10894

I have a Realm database with an object "Events" with a property name "occasion" of type string, a property name "venue" of type string and the other one is "date" of type Date(). My question is how do we query the occasions which have a date the same as today (current day). I have tried this code to retrieve the occasions using the date filter and it does work:

let date = Date()
let dateFormatter = DateFormatter()
// dateFormatter.dateFormat = "dd/MM/yyyy"
dateFormatter.dateFormat = "d MMM yyyy"
let stringDate = dateFormatter.string(from: date)        

todayEvent = realm.objects(Events.self).filter("date = '\(stringDate)'").sorted(byKeyPath: "venue", ascending: true)

The date in the Realm database shows a date in this format "17 Jan 2019 at 12:00:00 AM" even when the date was saved in the format of dd/MM/yyyy. I've tried changing the date format but still, it won't work. Or do I need to set a format for the date property when I declare it in the first place? If so how do I do this in a Realm class object? I've tried but it gave an error. I'm stuck here now. Please help

This is how I saved the data to Realm database :

@IBAction func addNewEventButtonPressed(_ sender: UIButton) {



    let newDataEntry = Events()

    newDataEntry.occasion = occasionTextFile.text!


    let dateString =  dateTextField.text!
    let dateFormatter = DateFormatter()
    dateFormatter.dateFormat = "dd/MM/yyyy"
    let dateFromString = dateFormatter.date(from: dateString)

    newDataEntry.date = dateFromString!

    do {
        try realm.write {
            realm.add(newDataEntry)
        }
    } catch {
        print("Error saving data \(error)")
    }


}

3 Answers

According to Realm docs (emphasis mine):

The comparison operators ==, <=, <, >=, >, !=, and BETWEEN are supported for Int, Int8, Int16, Int32, Int64, Float, Double and Date property types

Thus, the following should work:

let date = Date()
todayEvent = realm.objects(Events.self)
    .filter("date == %@", date)
    .sorted(byKeyPath: "venue") // sorted in ascending order by default

You are using Date object which is with time & locale, so to use only with date can use below function for removing time & locale from date object:

   func reomveTimeFrom(date: Date) -> Date {
            let components = Calendar.current.dateComponents([.year, .month, .day], from: date)
            let date = Calendar.current.date(from: components)
            return date!
        }

Now use this function to get exact date object & fetch data from realm database.

let today = self.reomveTimeFrom(date: Date())
let predicate = NSPredicate(format: "date ==  %@",today as NSDate)
let results = realm.objects(Events.self).filter(predicate)

Complete source code on github

The issue is you're storing Date objects but trying to filter that property as a string.

This line

let dateFromString = dateFormatter.date(from: dateString)

creates a Date() object from a string and that's what's stored in your Realm property.

A Realm date object looks like this

2019-01-18T05:00:00.000Z

Here's your filter:

let stringDate = dateFormatter.string(from: date)
todayEvent = realm.objects(Events.self).filter("date = '\(stringDate)'")

This is creating a string from a date object and Realm won't know what to do with it. If you want to filter a date property, use a date object to do so.

Importantly though - that line should crash with the following error

Expected object of type date for property 'date' on object of type 'Event', but received: 18 Jan 2019

The solution is that if the property your searching on is a Date, then the filter needs to be a Date object.

So an example of writing a date object that starts as a mm/dd/yyyy string

let aDateString = "01/20/2019"
let mmddyyyyFormatter = DateFormatter()
mmddyyyyFormatter.dateFormat = "MM/dd/yyyy"
if let dateObjectToStore = mmddyyyyFormatter.date(from: aDateString) {
    newDataEntry.date = dateObjectToStore
} else {
    print("could not create date object")
}

Suppose we now want to find that object with a string of "yyyymmdd"

let dateStringToFind = "20190120"
let yyyymmddFormatter = DateFormatter()
yyyymmddFormatter.dateFormat = "yyyyMMdd"
let dateObjectToFind = yyyymmddFormatter.date(from: dateStringToFind)
let results = realm.objects(MyObject.self).filter("date == %@", dateObjectToFind)
if results.count > 0 {
    for myObject in results {
        print(myObject)
    }
} else {
    print("no objects have that date")
}

as you can see, we stored a date as a date object and then when filtering, filtered for a date object.

There are some caveats to filtering and some exceptions but you get the general idea.

Related