I have an app that uses Core Data. I have a function
func showHistory(for days:Int)->Int {
var historyForXDaysInt = Int()
var calendar = Calendar.current
calendar.locale = .current
calendar.timeZone = .current
var startDate:Date?
var endDate = Date()
switch days{
case 1:
startDate = Date()
startDate = calendar.startOfDay(for: startDate!)
default:
startDate = calendar.date(byAdding: .day, value: -days+1, to: Date())
startDate = calendar.startOfDay(for: startDate!)
}
//There is more to this function, where I make the request but I just included the relevant part for my question
}
My question: I am trying to get relevant data from Core Data between two dates via the predicate let predicate = NSPredicate(format: "(completedDate >= %#) AND (completedDate <= %#)", startDate! as NSDate, endDate as NSDate).There is a completedDate property in my core data model. When I make the request I get data for 1 more day than I asked for. I tried solving it with adding 1 to the days variable. For example, if I am asking for 2 days of data (meaning days = 2) I get data for 3 days. What might be the cause? Why does it not work as intended without the +1 ?
I am using Swift 3, and i would like to print the every day between two dates.
For example:
08-10-2017 -> Start Date
08-15-2017 -> End Date
Should print:
08-10-2017
08-11-2017
08-12-2017
08-13-2017
08-14-2017
08-15-2017
I want to get ranges in two specific date, can someone help me out please. I tried to put these two dates to for loop but no chance.
You need to create a calendar based dates, and start increasing start date until you reach end date. Here is a code snippet, how to do it:
func showRange(between startDate: Date, and endDate: Date) {
// Make sure startDate is smaller, than endDate
guard startDate < endDate else { return }
// Get the current calendar, i think in your case it should some fscalendar instance
let calendar = Calendar.current
// Calculate the endDate for your current calendar
let calendarEndDate = calendar.startOfDay(for: endDate)
// Lets create a variable, what we can increase day by day
var currentDate = calendar.startOfDay(for: startDate)
// Run a loop until we reach the end date
while(currentDate <= calendarEndDate) {
// Print the current date
print(currentDate)
// Add one day at the time
currentDate = Calendar.current.date(byAdding: .day, value: 1, to: currentDate)!
}
}
Usage:
let today = Date()
let tenDaysLater = Calendar.current.date(byAdding: .day, value: 10, to: today)!
showRange(between: today, and: tenDaysLater)
I want to filter entries from my database by single date components such as month=12. Notice that I can't query for entries with dates between to fixed dates because the year might be different.
It should look/work like this:
let request = NSPredicate(format: "date.month=%i", 12)
You will have to use a time interval for the predicate.
NSPredicate(format: "date >= %# && date < %#", aDate, aDate.date(byAddingMonths:1))
Please note that you perhaps mixed up the NSFetchRequest with the NSPredicate in your question.
To add a month:
extension Date {
func date(byAddingMonths months: Int) -> Date {
var diff = DateComponents()
diff.month = months
return Calendar.current.date(byAdding: diff, to: self)!
}
}
EDIT
After clarifying the question, it appears you need a month attribute. Add an Int attribute to your entity that should be filled out with the month automatically whenever you change the date (override didSet). Here is how you get the month from the date:
let month = Calendar.current.component(.month, from:theDate)
I am trying to return ONLY tomorrow's data using Swift and Core Data but it returns both today's and tomorrow's together. Any idea why? Below is the code I am using. Thank you in advance!
let today = NSDate()
let tomorrow = NSCalendar.currentCalendar()
.dateByAddingUnit(
.Day,
value: 1,
toDate: today,
options: []
)
todoTomorrow = CoreDataManager.getData("ToDos", predicate: NSPredicate(format:"dueDate<%#", ((tomorrow))!)) as! [ToDos]
You need to modify your predicate to be dueDate < %# AND dueDate > %# with parameters tomorrow and today so it checks both the upper and lower allowed range of the dates.
I want to search through all existing objects to see if there are any matching date objects in CoreData:
Dates are currently saved in my CoreData via the start_date attribute with the following format: 2013-08-29 14:27:47 +0000.
I am then letting a user select a date from a UIDatePicker and assigning .date() to variable date.
e.g. My Selected date = 2013-08-29 17:34:23 +0000.
Below is how i search CoreData, using a predicate.
let predicate = NSPredicate(format: "start_date contains[search] %#", date)
let request:NSFetchRequest = NSFetchRequest(entityName: "Project")
let sortDescriptor:NSSortDescriptor = NSSortDescriptor(key: "number", ascending: true)
let sortDescriptorsAry:NSArray = [sortDescriptor]
request.sortDescriptors = sortDescriptorsAry
request.predicate = predicate
return request
However i get no results. I assume this because both attributes don't match because of the time:
start_date = 2013-08-29 14:27:47 +0000
date = 2013-08-29 17:34:23 +0000
How can i some how tell CoreData to ignore the "177:34:23 +0000" bit, or is there a better way?
Edit:
I do have the option to change the way in which the date format is stored initially:
I have tried this:
var now:NSDate = self.startDatePicker.date
var calendar:NSCalendar = NSCalendar(calendarIdentifier: NSGregorianCalendar)
var components:NSDateComponents = calendar.components(NSCalendarUnit.CalendarUnitYear | NSCalendarUnit.CalendarUnitMonth | NSCalendarUnit.CalendarUnitDay, fromDate: now)
components.hour = 00
components.minute = 00
components.second = 00
var newDate:NSDate = calendar.dateFromComponents(components)!
However on some days, my time is being set to a day before.
For Example:
Selecting the 30th August, after transform I get 2014-08-29 23:00:00 +0000
Before to save start_date attributes in Core Data, you need to be sure that their time is set to 12:00 AM:
//Get "Aug 29, 2014, 12:00 AM" from "Aug 29, 2014, 10:07 PM"
let newDate = NSDate() //or any other date
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone.systemTimeZone()
var startDate: NSDate?
var duration: NSTimeInterval = 0
calendar.rangeOfUnit(.DayCalendarUnit, startDate: &startDate, interval: &duration, forDate: newDate)
//Create, set and save a new managedObject
//Records, here, is the name of your NSManagedObject subclass
let record = NSEntityDescription.insertNewObjectForEntityForName("Records", inManagedObjectContext: managedObjectContext) as Records
record.start_date = startDate
/* set other attributes here */
var error: NSError?
if !managedObjectContext.save(&error) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
println("Unresolved error \(error), \(error!.userInfo)")
abort()
}
Then, you will be able to fetch a date in Core Data with the predicate you want this way:
//Set your datePicker date to 12:00 AM
let pickerDate = dateFromMyDatePicker //as NSDate
let calendar = NSCalendar.currentCalendar()
calendar.timeZone = NSTimeZone.systemTimeZone()
var predicateDate: NSDate?
var duration: NSTimeInterval = 0
calendar.rangeOfUnit(.DayCalendarUnit, startDate: &predicateDate, interval: &duration, forDate: pickerDate)
//Create your fetchRequest
/* ... */
//Set your predicate
let predicate = NSPredicate(format: "start_date == %#", predicateDate)
Edit
As an alternative to rangeOfUnit:startDate:interval:forDate:, you can use the code you provide. But in both cases, don't forget to add the following line:
calendar.timeZone = NSTimeZone.systemTimeZone()
If you still have to option of changing the way you store data, I would store it on your project-entity as three attributes: an Integer for year, an Integer for month and an Integer for day. It will be easier to code (you can create a predicate like:
NSInteger year = ....
NSInteger month = ....
NSInteger day = ....
[NSPredicate predicateWithFormat: #"day == %# AND month == %# and year == %#", #(day), #(month), #(year)];
) and this probably work quicker compared to using a string. If you want to go with the string (beware extraneous spaces or other interpunction!), you can in code construct a string with the format 'yyyy-mm-dd' (use NSString stringWithFormat:) and then use an NSPredicate like:
NSInteger year = ....
NSInteger month = ....
NSInteger day = ....
NSString * dateString = [NSStringWithFormat: #"%4i-%2i-%2i", year, month, day];
[NSPredicate predicateWithFormat: #"dateString contains %#", dateString];
However, this will only work if YOU constructed the string that you stored, not the NSDate.description(). If you store actual NSDate, see my first original answer :).
But really, if you still have an option, don't store it as a string. It wil really be problematic, if not now, then later (I can tell from experience).
<edit>
Your second way of doing things seems to me like a good way to go. Store the integer values for year, month and day, and create a predicate with that. Your usage of NSDateComponents and calendar is great, and so this should be the easiest way to go. Very important: don't use for the time of 00, as midnight is a tricky time (there is daylight savings times in parts of the world, there is the question of whether exactly midnight is part of the previous of the next day). Just enter 12 as the time, to be as far away as you can from midnight.
Also, i would advice you to watch the WWDC from 2011 'Performing Calendar Calculations, Session 117. It talks among other very interesting things about why midnight is tricky (if not, then that is explained in the 2013 session :) ).
</edit>
NSDate has sub-second accuracy. It's description method (that gets called when you NSLog an NSDate) only displays second-accuracy.
So to use an NSDate in an NSPredicate, always specify 'larger/smaller then or equal to' operators.
For example:
NSDate * lastWeek = // ... created using NSCalendar and NSDateComponents.
NSDate * now = [NSDate date];
NSPredicate * entitiesFromAfterLastWeekAndBeforeNow = [NSPredicate predicateWithFormat: #"start_date >= %# AND start_date <= %#", lastWeek, now];
I would advice against storing a data in something else the a native NSDate, as you might store dates that are incorrect due to conversions. Also, storing dates as string will make queries a lot slower (as strings have to be parsed, and NSDate is simply a number). But that is another discussion, and you might want to ship, so you have to do what you think is best.
Edit: I'm sorry, only now noticed that you are writing Swift. But I think my example is clear enough that it is convertible to Swift, right?