ALAssetPropertyDate returns "wrong" date - objective-c

I'm currently working on a project in which i need to read some (Latitude, Longitude and date ) EXIF data. The location data seems correct, but the date i'm getting seems to be the "date last modified" date.
{
CLLocation *loc = [asset valueForProperty:ALAssetPropertyLocation];
NSDate *date = [asset valueForProperty:ALAssetPropertyDate];
//Returns Last modified date(Picture was taken ... let's say september
//last year and it would return the date and time I 'modified' the image).
NSString *latitude = [NSString stringWithFormat:#"%g",loc.coordinate.latitude];//Returns correct Latitude
NSString *longitude = [NSString stringWithFormat:#"%g",loc.coordinate.longitude];//Returns correct Longitude
}
My question is: Am i doing something terribly wrong, or is this expected behavior.
I also tried to use the loc.timestamp instead of the [asset valueForProperty:ALAssetPropertyDate] but these returned the same date.
Any help is greatly appreciated !

You can also get Exif DateTimeOriginal through ALAsset.
NSDateFormatter *dateFormatter = [[NSDateFormatter new] autorelease];
dateFormatter.dateFormat = #"y:MM:dd HH:mm:ss";
NSDate *date = [dateFormatter dateFromString:[[[[asset defaultRepresentation] metadata] objectForKey:#"{Exif}"] objectForKey:#"DateTimeOriginal"]];
Getting metadata from asset requires to load Exif header on memory (or entire image file?) and those methods above seems to use autorelease pool for the memory spaces. This may cause memory shortage or worse crash if you do a batch process for thousands of images.
To work around for memory shortage, you may use Ad-Hoc autorelease pool.
NSDateFormatter *dateFormatter = [[NSDateFormatter new] autorelease];
dateFormatter.dateFormat = #"y:MM:dd HH:mm:ss";
for (ALAsset *asset in thousandsOfAssets) {
NSAutoreleasePool *pool = [NSAutoreleasePool new];
NSDate *date = [dateFormatter dateFromString:[[[[asset defaultRepresentation] metadata] objectForKey:#"{Exif}"] objectForKey:#"DateTimeOriginal"]];
// do something
[pool release];
}
EDIT: correct wrong dateFormat (SS -> ss). Thanks #code-roadie

Though it's not explicitly documented, I'm guessing that this is the expected behavior. The date refers to when the asset was created and when you're modifying the image, you're probably implicitly creating a new asset. Nothing in the ALAsset documentation suggests that its properties correspond to the image's EXIF data.
To access the EXIF data, you could use the Image I/O framework (available since iOS 4.0), specifically the CGImageSourceCopyProperties function.

Related

NSDateFormatter dateFromString returns nil for parsing EXIF data

I am trying to parse data from EXIF date to NSDate. Here is a sample date string taken from an image file:
2013:10:15 19:19:31
It is in year:month:day 24hour:minute:second format. I am using the following code to parse this:
NSDateFormatter *formatter = [[NSDateFormatter alloc] initWithDateFormat:#"yyyy:MM:dd HH:mm:ss" allowNaturalLanguage:YES];
formatter.formatterBehavior = NSDateFormatterBehavior10_4;
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
NSDate *date = [formatter dateFromString:dt];
If I don't set the formatter behavior it complains about trying a locale identifier of 10.4 on an 10.0 formatter. (I'm on Mavericks, I think it's a new issue on Mavericks) Anyway I've tried combinations of not setting locale at all, setting locale to en_US_POSIX instead of just en_US, setting a timezone for formatter, changing yyyy to YYYY in date format, and allowNaturalLanguage to NO, however, dateFromString always returns nil. My app is running on OS X Mavericks, my system's language is English, and my region is set to Turkey if it matters. I want my app to be English-only, independent of users' region (including the date formats). I've seen this question: Parsing EXIF date string to NSDate but even I try the exact steps, formatter returns nil for dateFromString. What is wrong with the date formatter?
It seems to be a problem of the initWithDateFormat initializer.
According to the documentation, it creates a formatter that uses the "OS X v10.0 formatting behavior", but I have no idea what that is. And setting the behavior to NSDateFormatterBehavior10_4 explicitly seems also not to help.
Using
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy:MM:dd HH:mm:ss"];
NSDate *date = [formatter dateFromString:#"2013:10:15 19:19:31"];
produces the expected result.

UIDatePicker for time returns very strange results

I've searched the forum and there are quite a few posts on strange behavior of the UIDatePicker, but none of the solutions solved my problem.
I have a simple UIDatePicker initialized like this:
self.picker = [[UIDatePicker alloc] init];
self.picker.datePickerMode = UIDatePickerModeTime;
self.picker.calendar = [NSCalendar currentCalendar];
self.picker.timeZone = [NSTimeZone localTimeZone];
self.picker.date = self.initialDate;
[self.view addSubview:self.picker];
This is done in the viewDidLoad method of a simple view (no xib, no interface builder, just plain code).
Now I have a button, which retrieves the date and "processes" it.
NSDate* selected = self.picker.date;
NSLog(#"%#", selected);
NSDateComponents* components = [self.picker.calendar components:(NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit) fromDate:selected];
NSLog(#"0001-01-01 %02i:%02i:%02i +0000", components.hour, components.minute, components.second);
[components setSecond:0];
NSLog(#"0001-01-01 %02i:%02i:%02i +0000", components.hour, components.minute, components.second);
selected = [self.picker.calendar dateFromComponents:components];
NSLog(#"%#", selected);
Since the date picker will not allow the user to set seconds, I just want to reset them to zero (because I need to compare dates later).
When I select "16:00" (which is 4:00 pm) The log output is:
2012-05-21 17:43:48.428 Green Thumb[26828:fb03] 0001-01-01 14:55:36 +0000
2012-05-21 17:43:48.428 Green Thumb[26828:fb03] 0001-01-01 16:00:56 +0000
2012-05-21 17:43:48.429 Green Thumb[26828:fb03] 0001-01-01 16:00:00 +0000
2012-05-21 17:43:48.429 Green Thumb[26828:fb03] 0001-01-01 14:54:40 +0000
So the original date taken from the picker is completely off. The time taken from the components (which is created based on the date from the picker) is correct, as is the corrected time. But when I put the components back in a NSDate instance, it all gets messed up again (but differently).
Me and google we are both out of ideas, so I hope we can create some new ones here!
Thanks in advance, LetzFlow
PS: I'm testing this with the iPhone 5.1 Simulator.
EDIT #1:
I've tracked down the actual problem, but I'm stuck again:
I've tracked it down to the following point: I'm storing the dates in the NSUserDefaults object.
NSUserDefaults defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:[NSNumber numberWithFloat:[time timeIntervalSince1970]] forKey:#"notification.time"];
And here I get them out again:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSNumber num = [defaults valueForKey:#"notification.time"];
NSDate* time = [NSDate dateWithTimeIntervalSince1970:[num floatValue]];
Interesting enough: What goes in, is not what comes out! But why? :)
EDIT #2:
And last but not least I've solved it myself. Thankfully, because it's the most stupidest mistake ever.
NSTimeInterval is not a float but a double. And while up until now I never ran into any problems with using a float saving it in the NSUserDefaults seems to have brought forward the errors.
Your NSLog statements seem to have the wrong format (i.e. the "0001-01-01" part). Try this instead:
NSString *output = [NSDateFormatter localizedStringFromDate:selected
dateStyle:NSDateFormatterShortStyle
timeStyle:NSDateFormatterShortStyle];
NSLog(#"%#", output);

EXC_BAD_ACCESS(code=2) on [[NSDateFormatter alloc] init]

Since iOS 5.1 I'm getting EXC_BAD_ACCESS(code=2) errors when I instantiate an NSDateFormatter object.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; // bad_access
[dateFormatter setDateFormat:#"dd.MM.yyyy"];
Did anyone experience something similar or maybe even has a solution? It's making me crazy!
I'm using ARC for my project.
EDIT:
Even NSDateFormatter* dateFormatter = [NSDateFormatter new]; is giving me the same error.
I've had some similar problems some time ago. Ordinary 100%-error-free line causes this EXC_BAD_ACCESS, code=2, zombies show nothing.
The thing was that I had a death cycle, somehow calling function A caused calling function A again, that caused calling function A again and so on (at the stack trace there were a looooot of lines). So I've just ran out of memory and got EXC_BAD_ACCESS. Preventing code to enter that death loop solved that for me.
Hope this helps.

Format of date when retrieving lastModified

I am comparing the date when a file was last modified for two files, one local and one on Amazon S3 server. I am using the AWS IOS SDK framework and can successfully request and receive response from the S3 server but I have trouble understanding the format of the returned s3 date.
On my local machine the date format for lastModified is "2011-07-21 18:43:15 -0400" while for the file residing on the S3 server it is "2011-10-15T16:25:49.000Z".
My local info is obtained using:
NSFileManager *fm = [NSFileManager defaultManager];
NSDictionary *attr = [fm attributesOfItemAtPath:filePath error:nil];
NSDate *localDate = [attr objectForKey:NSFileModificationDate];
while my S3 info is obtained using
for (S3ObjectSummary *object in [listObjectsResult objectSummaries]) {
NSDate *s3date = [object lastModified];
}
Does anyone know if I can convert the date for the S3 file to a format that I can use to compare these two dates using:
NSTimeInterval deltaSeconds = [s3Date timeIntervalSinceDate: localDate];
or am I doing something wrong here? Right now my program crashes with
[NSCFString timeIntervalSinceReferenceDate]: unrecognized selector sent to instance 0x200351360.
probably because the s3 date format is not in proper format. I am quite new to using the AWS S3 SDK so all help is greatly appreciated. If anyone also knows of some good tutorials for this framework (apart from the demo code that comes with it), that would be great. Cheers, Trond
It looks to me as [object lastModified] simply returns a NSString and not an NSDate object, as stated in the documentation.
NSDateFormatter can be used in this case to create a NSDate object from the string:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'+'mm':'ss.SSS'Z'"];
NSDate *s3date = [dateFormatter dateFromString:[object lastModified]];
[dateFormatter release];
The Date Formatting guide has lots of handy examples. You may need to tweak the format string slightly as i have not tested it.
Had some trouble with the given answer - seems that there is some sort of duplicated 'ss'+'mm' stuff in there that prevents the string from correctly translating. Additionally, because the time zone in the string is given as UTC, you have to set the time zone for the formatter. Here is the code I used (that worked for me), I turned it into a category, and default to using the previously provided string in the event the one that works for me doesn't work for you:
#interface S3ObjectSummary (lastModifiedDate)
- (NSDate *)lastModifiedDate;
#end
#implementation S3ObjectSummary (lastModifiedDate)
- (NSDate *)lastModifiedDate
{
NSString *modifiedString = [self lastModified];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; // Note, this is ARC, add autorelease if you aren't ARC
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"UTC"]];
[dateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'"];
NSDate *modifiedDate = [dateFormatter dateFromString:modifiedString];
if(!modifiedDate) // Try other format in case of fail http://stackoverflow.com/a/7799607/285694
{
[dateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'+'mm':'ss.SSS'Z'"];
modifiedDate = [dateFormatter dateFromString:modifiedString];
}
return modifiedDate;
}
#end
Looks like this also works:
NSDate *summaryDate = [NSDate dateWithISO8061Format:summary.lastModified];

Fetch from Core Data sorted by formatted date

I am having trouble fetching results from Core Data that are sorted by date.
I have a DB table that contains football matches. Each match has a homeTeam, awayTeam and kickoffTime. The kickoffTime is an NSDate that stores the date and time the match will start.
I want to display the results of a query in a TableView divided into sections by the kickoff date. With the date as the section heading.
This is a little more complex than it might first appear. Due to differing time zones a match starting on one date in one part of the world is actually starting on a different date in another part of the world. So I can't simply ignore the times and store the kickoff dates in another column.
What I'm trying to do create a custom accessor that returns a formatted date, in whatever time zone the user is in, and then use this to sort and section the results. Here's my code in Match.h:
#dynamic kickoffTime;
#dynamic formattedKickoffTime;
#dynamic dateFormatter;
- (NSString *)formattedKickoffTime
{
[self willAccessValueForKey:#"kickoffTime"];
// Set the date formatter to the format we want to display the date
[dateFormatter setDateFormat:#"ccc, d MMM"];
// Format the date
NSString *myFormattedKickoffTime = [dateFormatter stringFromDate:[self kickoffTime]];
[self didAccessValueForKey:#"kickoffTime"];
// return the formatted date
return myFormattedKickoffTime;
}
- (NSDateFormatter *)dateFormatter
{
if (dateFormatter == nil)
{
dateFormatter = [[NSDateFormatter alloc] init];
}
return dateFormatter;
}
#end
However when I try to fetch and sort the data like so:
NSSortDescriptor *kickoffDescriptor = [[NSSortDescriptor alloc] initWithKey:#"formattedKickoffTime" ascending:YES];
...
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:appDelegate.managedObjectContext sectionNameKeyPath:#"formattedKickoffTime" cacheName:nil];
I get the following error message:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'keypath formattedKickoffTime not found in entity <NSSQLEntity Match id=1>'
Would someone offer some advice please?
So I've found a way to do this. First of all I keep all the dates and calculations in the model in GMT. I add a custom accessor (kickoffDate) to my Match entity that gets the kickoffTime, sets it's time to 00:00:00 and returns this. One thing I had to do, that wasn't obvious, was set the timeZone of the date returned by the custom accessor to GMT as otherwise this was being set to the user's system timeZone.
As all the dates returned by the model are in GMT everything just seems to work as it should no matter what timeZone I set in the iPhone's Settings. I guess Core Data is smart enough to do the timeZone adjustments before it puts the matches in the sections.
I do have one question though, is [NSTimeZone timeZoneForSecondsFromGMT:0] the best way to get a 0 timeZone, by this I mean a timeZone that isn't affected by daylight saving time?
Oh and do I have to call [self willAccessValueForKey:#"kickoffTime"]; and [self didAccessValueForKey:#"kickoffTime"];? I don't really understand what these are doing!
Here's the code in Match.h:
...
#dynamic homeTeam;
#dynamic awayTeam;
#dynamic kickoffTime;
- (NSDate *)kickoffDate
{
[self willAccessValueForKey:#"kickoffTime"];
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components = [cal components:(NSYearCalendarUnit|NSMonthCalendarUnit|NSDayCalendarUnit) fromDate:[self primitiveValueForKey:#"kickoffTime"]];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
[components setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[self didAccessValueForKey:#"kickoffTime"];
return [cal dateFromComponents:components];
}
And then my fetch code includes the following a sort on kickoffTime and the sectionNameKeyPath is set to kickoffDate:
NSSortDescriptor *kickoffDescriptor = [[NSSortDescriptor alloc] initWithKey:#"kickoffTime" ascending:YES];
...
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:appDelegate.managedObjectContext sectionNameKeyPath:#"kickoffDate" cacheName:nil];