Objective-C - how to find objects with prop equal to date? - objective-c

I'm trying to filter an array of objects selecting those where a property of type NSDate are equal to a specific date. However, I only want to compare the date and ignore any time difference.
So I'm using an NSPredicate as so:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"PropertyWithDate == %#",
[NSDate date]];
As both the object is storing the date with time and the value for now I am passing holds both. How do I arrange it to ignore the time part in both during the comparison?

Remember, an NSDate expresses a point in time, independent of timezones, so it only makes sense to talk about the date part of an NSDate with respect to a specific calendar and timezone.
The short answer is that you'll need to construct two NSDates: one for 12am the day of your date, and one for 12am the day after, and then set up your predicate to look for dates between the two:
NSPredicate *firstPredicate = [NSPredicate predicateWithFormat:#"PropertyWithDate > %#", firstDate];
NSPredicate *secondPredicate = [NSPredicate predicateWithFormat:#"PropertyWithDate < %#", secondDate];
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubPredicates:[NSArray arrayWithObjects:firstPredicate, secondPredicate, nil]];
To construct firstDate and secondDate, you'll want to use NSCalendar's components:fromDate: and dateFromComponents: methods. If you want to use the device's current default calendar and timezone, you can just use [NSCalendar calendar] to get a calendar to use.

You should use NSDatecomponents to do the compare. There is a great video about date math and the libraries in the WWDC 2011 videos.
See the NSDateComponents class reference for more information.

Related

Filter core data objects by time

Hello I am trying to filter for only objects that fall under a certain time. For example, I only want objects that are before 8 AM or after 1PM.
If in my object there is a createdDate attribute and I only want to fetch objects that were created before 8AM, what would be the approach to do that?
I wanted to do it by using either of these approaches but have been getting 0 results trying out both approaches.
1.Created a separate derived attribute that returns the time portion and do a filter on that.
i.e. (Persistent)createdDate -> (Derived)createdTimeByHour
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"createdTimeByHour < 10"];
2.Setup predicate to filter based on converted createdDate.
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"createdDate.hour < 10"];
Attempts:
I've tried to implement the first solution, but NSFetchRequest doesn't return to me any results. I create the derived attribute like so...
- (NSNumber *)createdTimeByHour
{
NSNumber *result;
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:[self createdDate]];
result = #([components hour]);
return result;
}
BACKGROUND PROBLEM
I found I was getting 0 results because I added derived data after there was already existing objects in Core Data. These objects don't have a value in core data, but when trying to log for it after fetching all results, it creates the derived result, but never saves or stores data in core data. I was hoping I wouldn't need to store this in Core Data, but in the end, it makes sense to do this since I will be querying by this field often.
SIMPLE SOLUTION
So in a very simple case, in older to filter by time, you must create an attribute that stores this value in core data. You cannot filter by time straight from NSDate to my knowledge.
ADVANCED SOLUTION
If you are adding this field when there already exists objects in core data, you must process each object in core data to store this result.
I think the easiest way for Core Data is to use solution 1, a derived property to return the hour of the day. You could also persist it (which you would need to do to use it in a NSFetchedResultsController); you can do this automatically whenever the createdDate is set (e.g. in awakeFromInsert).
E.g.
-(void) awakeFromInsert {
[super awakeFromInsert];
self.createdDate = [NSDate date];
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *comps =
[calendar components:NSCalendarUnitHour fromDate: [NSDate date]];
self.hour = #(comps.hour);
}

Format Duration as ISO8601 Period with NSDateIntervalFormatter

I am trying to convert the duration between two dates to the format "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.S'S'". An example of my expected output is: P0Y0M0DT0H0M0.002S (this is port of an Android project that uses DurationFormatUtils).
My test case:
NSDateIntervalFormatter *durationFormatter = [NSDateIntervalFormatter durationFormatter];
NSDate *date = [NSDate date];
NSDate *end = [date dateByAddingTimeInterval:25];
NSString *duration = [durationFormatter stringFromDate:date toDate:end];
XCTAssertTrue( [duration hasPrefix:#"P"], #"Actual String: %#", duration );
My formatter is defined as:
+ (NSDateIntervalFormatter*) durationFormatter {
static NSDateIntervalFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateIntervalFormatter alloc] init];
formatter.dateTemplate = #"'P'yyyy'Y'M'M'd'DT'H'H'm'M's.S'S'";
});
return formatter;
}
When I run this, duration is 3/5/2015, 16:46:20.3. If I set the locale on the formatter to nil and set the date and time format to NSDateIntervalFormatterFullStyle, the duration has the following format: Thursday, March 5, 2015, 4:51:36 PM GMT-05:00 - Thursday, March 5, 2015, 4:52:01 PM GMT-05:00.
How can I format the duration between two dates with ISO8601 period format (or at least as "'P'yyyy'Y'M'M'd'DT'H'H'm'M's.S'S'")?
The class you are using, NSDateIntervalFormatter, is designed to produce intervals of the form date1 - date2, not format the time difference between two dates.
To get what you are after you need to use NSCalendar, NSDateComponents and stringWithFormat: (or similar).
First you need a calendar, [NSCalendar currentCalendar] is a good choice. (The difference between two absolute points in times can be a different when expressed in the units of a particular calendar - if that doesn't make sense don't worry, just use currentCalendar!)
Next use the components:fromDate:toDate:options: method to obtain an instance of NSDateComponents which contains the number of years, days, hours etc. between your two dates.
Finally use stringWithFormat: to format the components as you wish.
HTH
You have misunderstood what NSDateIntervalFormatter is for. It's for formatting an interval as a string of the form start - end.
It's not for formatting a duration.
You can use NSDateComponentsFormatter to format a duration. However, I don't know if it supports the specific format you want.
It has the convenience methods -stringFromDate:toDate: and -stringFromTimeInterval:.

Puzzled with NSPredicate predicateWithFormat table

both lines of code work but not as I would expect. The first one, using a variable skips only the first item of the table and updates all the remaining items. Using a string literal in the second option doesn't and works the way I want. Why is this happening??
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"myString == %#", date];
OR
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"myString == '5/24/14'"];
To see why this is happening, try logging date:
NSLog(#"%#", date);
You will see that the resulting string (in the console) does not look at all like '5/24/14'! But that (what you see in the console) is the string that you are using in your first code.
So it's all about how the NSDate is covered to an NSString. How to get around this? Use an NSDateFormatter to convert the date to a string in the form you're after!

Formatting NSDate

I am having some trouble comparing NSDate as they have a different format.
From one side I have a NSDate who looks like this:
2013-12-05T10:12:00.120Z
And from the other side I have another NSDate that looks this way:
2013-12-01 10:1200 +00000
My question is, how could I make the first NSDate look like the 2nd one?
And more important, what does 120Z mean? I guess it's the timezone, but I am not really sure of it.
By the way, is it there any way to can format the NSDate's and updating the time respecting the timezone hour difference?
Thanks a lot!
EDITED:
To get the 1st NSDate I do the following (I need to get the last opened date of a file):
MDItemRef item = MDItemCreate(NULL, (__bridge CFStringRef)filePath);
NSDate *date = (NSDate*)CFBridgingRelease(MDItemCopyAttribute(item,
kMDItemLastUsedDate));
And to get the 2nd NSDate I do the following:
NSDate* threeDaysAgo = [NSDate dateWithTimeIntervalSinceNow:-259200];
Convert both the dateStrings to NSDate and then you can easily compare the dateObjects.
For converting string to date thing you need :
NSDateFormatter
For comparing two dates :
resultant = [dateOne compare:dateTwo]
resultant can be NSOrderedAscending or NSOrderedSame or NSOrderedDescending.
You have a misunderstanding of what an NSDate is. It is not "in a format" at all, but is actually a wrapper around a a double which is the number of seconds since Jan 1st 1970 12:00am UTC. You can compare your two dates directly to see which one is the earlier. However, if you are trying to compare for equality, it's more tricky. If you want to see if they are within one minute of each other, you can do something like
[date1 timeIntervalSinceDate: date2] < 60.0;

In Objective-C, to get the current hour and minute as integers, we need to use NSDateComponents and NSCalendar?

I'd like to get the current hour and minute as integers. So if right now is 3:16am, I'd like to get the two integers: 3 and 16.
But it looks like [NSDate date] will give the number of seconds since 1970, or it can give a string of the current time representation, but there is no easy way to get them as integers?
I see a post in Getting current time, but it involved NSDateComponents and NSCalendar? That's way too complicated... all that was need is something like
NSDate *date = [NSDate date];
int hour = [date getHour]; // which is not possible
Is there a simpler way than using 3 classes NSDate, NSDateComponents, and NSCalendar to get the current hour as an integer, or typically, in Objective-C, would we typically still use C language's localtime and tm to get the hour as an integer?
How you interpret the seconds since 1970 depends on the calendar that you are using. There is simply no other option. Fortunately it is not that difficult to set up. See the 'Data and Time Programming Guide' for lots of examples. In your case:
// Assume you have a 'date'
NSCalendar *gregorianCal = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *dateComps = [gregorianCal components: (NSHourCalendarUnit | NSMinuteCalendarUnit)
fromDate: date];
// Then use it
[dateComps minute];
[dateComps hour];
So it really isn't that complicated.
Also note that you could create a 'Class Category' to encapsulate this as:
#interface NSDate (MyGregorianDateComponents)
- (NSInteger) getGregorianHour;
- (NSInteger) getGregorianMinute;
#end
NSDate just holds the time that has passed since a certain reference date, to get more meaningful numbers out of this (eg. after taking care of DST, leap years and all the other stupid time stuff), you have to use NSDateComponents with the appropriate NSCalendar.
My class can help.
https://github.com/TjeerdVurig/Vurig-Calendar/blob/master/Vurig%20Calendar/NSDate%2Bconvenience.m
I'm sure you can figure out the minute part :)