NSDate as keys for NSDictionary - cocoa-touch

Is it possible to add NSDate as keys and some arrays as it values in a NSDictionary?
I dont have any requirement of writing the dictionary to disk - archiving or unarchiving, just as this guy needs it: NSDictionary with NSDates as keys
But, why I want NSDate to be added as keys specifically is so that I can fetch all keys from the dictionary and do some computations like, fetching the dates from within a range.
Whether two objects with same date value share same memory when created?
Is it possible to have NSDate as key? Also, what other problems I might face with this assumption?
Thanks,
Raj
Edit:
After posting my question, I just wrote a sample code:
NSDateComponents *dateComps = [[NSDateComponents alloc] init];
[dateComps setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[dateComps setDay:1];
[dateComps setMonth:1];
[dateComps setYear:2012];
NSDate *date1 = [[NSCalendar currentCalendar] dateFromComponents:dateComps];
NSDate *date2 = [[NSCalendar currentCalendar] dateFromComponents:dateComps];
NSLog(#"Date 1 = %x, Date 2 = %x", date1, date2);
Output:
Date 1 = 7945610, Date 2 = bd03610
But does the key comparison of NSDictionary interpret these 2 NSDate objects as different?
Another Edit:
Also, if I should convert NSDate to NSString using NSDateFormatter, I cannot directly check for dates within range. I will have to perform the following steps:
Get all NSString array of keys from dictionary
Convert them back to array of NSDate
Perform predicates and fetch dates within range
Again convert back those dates to an array of Strings
Now use this string keys to get values from dictionary!
So, is there any better way?

Yes, NSDate objects can be used as keys for NSDictionary - any object type can be used as a key provided it supports the NSCopying protocol. Keys are compared using isEqual: and not by pointer value. See the Overview section of the NSDictionary documentation for more details.

Related

Opposition of localizedStringFromDate: dateStyle: timeStyle:

When I format a date via:
[NSDateFormatter localizedStringFromDate:date dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterNoStyle]];
save it somewhere, then try to access it, i get a NSString. How to get it as a NSDate?
BTW: I want a method where i put NSDateFormatterStyle as argument. Otherwise it will be wrong- in different locale it will be saved as different string, so formatting it as
[dateFormat setDateFormat:#"yy/MM/dd"];
or any other options of this kind - will create an error. Or at least i think so ;).
Thanks for any responses.
NSDateFormatter returns a NSString representation of the NSDate object. NSDate is format insensitive, meaning it isn't tied to some locale, it's actually stored as a number. You use the NSDateFormatter to present that numerical value as a localized representation.
To convert a NSString back to an NSDate you can use something like this:
NSDateFormatter *df = [[NSDateFormatter alloc] init];
[df setDateFormat:#"yy/MM/dd"];
NSDate *myDate = [df dateFromString: dateString];
It sounds like you want to store the NSDate in a way that can be perfectly parsed both ways. In that case I'd recommend storing it as an ISO8601 date, and you'll need to use an nsdateformatter more custom-like to do that. There are plenty of libraries/categories out there on github or stackoverflow to help you.

How do I calculating the difference between two dates in Objective-C?

I'm working through a challenge in Objective-C Programming, The Big Nerd Ranch Guide, and I'm a little flummoxed by one of the challenges.
Use two instances of NSDate to figure out how many seconds you have been alive. Hint: here is how you create a new date object from the year, month, etc.:
So I need to the difference between now and my date of birth in seconds. Sounds good. Then the hint shows up:
#autoreleasepool {
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:1981];
[comps setMonth:7];
[comps setDay:12];
[comps setHour:1];
[comps setMinute:55];
[comps setSecond:33];
NSCalendar *g = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDate *dateOfBirth = [g dateFromComponents:comps];
The first issue is I don't understand what *g is meant to be. I know it's a pointer to an NSCalendar object. But why do we need it? And what is g meant to stand for?
The sample code then uses the g variable to grab a date. In another language, this would be as easy as DateDiff(dateOne, dateTwo, interval). I'm not clear on why the Calendar object is necessary in the first place, and why we have to create date components to feed the object.
This is all new to me, and I've worked with dynamic languages in the past. So a "dummies" like explanation would be great!
The sample code then uses the g variable to grab a date. In another language, this would be as easy as DateDiff(dateOne, dateTwo, interval). I'm not clear on why the Calendar object is necessary in the first place, and why we have to create date components to feed the object.
I'm not an Objective-C programmer, but I know a bit about date/time APIs.
In order to specify your birth date/time, you're giving a year of 1981, a month of 7 etc. What does a year of 1981 mean? To you, it may mean about 31 years ago... but to someone using a different calendar system, it could mean something entirely different. Converting from "year/month/day etc" to "point in time" is a bit like converting from a string to an integer: does "10" mean ten, or sixteen? It all depends on your frame of reference (the base, in this case - the calendar system in the date case).
The calendar - initialized as a Gregorian calendar - takes those date components and is able to give you back an NSDate which is a sort of "absolute" value in time.
As for computing the difference between two NSDate values - I suspect there's a member which gives you something like "seconds since the Unix epoch" (or possible milliseconds). So take that from both dates (your birth and "now"), subtract one from the other, and you'll get the elapsed number of seconds (or milliseconds) between the two.
EDIT: In fact, the timeIntervalSinceNow function is probably the one you want, once you've got your birth date.
You can do it this way
NSDateComponents *comps = [[NSDateComponents alloc] init];
[comps setYear:1981];
[comps setMonth:7];
[comps setDay:12];
[comps setHour:1];
[comps setMinute:55];
[comps setSecond:33];
NSDate *dateOfBirth = [[NSCalendar currentCalendar] dateFromComponents:comps];
NSTimeInterval timeGap=[[NSDate new] timeIntervalSinceDate:dateOfBirth ];

CoreData serialized NSDate convert back into real date

I using CoreData to store my objects that I fetch from a server.
I have a failure somewhere and cannot figure out what is going wrong. But it looks like it is related to the date stored in the CoreData database. So I opened the sqllite database and tried to compare the dates but since they are, for me, not human readable, is there any way to convert a serialized NSDate back into a human-readable date? (year-month-day for example)
I have the following dates in the CoreData database.
How can I convert them back into a human-readable date format? An online converted would be great since it is very difficult to write just an app for that.
Thanks and regards.
They are simply timestamps since interval reference date. Here's code you can use to decode them:
NSNumber *time = [NSNumber numberWithDouble:(d - 3600)];
NSTimeInterval interval = [time doubleValue];
NSDate *online = [NSDate dateWithTimeIntervalSinceReferenceDate:interval];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"MM/dd/yyyy HH:mm:ss.SSS"];
NSLog(#"result: %#", [dateFormatter stringFromDate:online]);
Why not just use NSLog to view your dates when your code is actually running? Run your fetch query and then output the dates in the debugger:
NSDate *myDate; // This is whatever you fetched from your database.
// Show myDate in the debug console.
NSString *output = [NSDateFormatter localizedStringFromDate:myDate
dateStyle:NSDateFormatterMediumStyle
timeStyle:NSDateFormatterMediumStyle];
NSLog(#"%#", output);

Problem formatting NSDate & NSString while using GData-Objectivec-client

I'm fighting with a strange situation: same code works different in two different projects. The one project is just empty command line utility with this code. The second project is with linked gdata-objectivec-client library.
Here is the code:
static NSString * const dateFormat = #"MM/dd/yyyy HH:mm:ss Z";
NSString *tmp_string = #"03/08/2011 10:07:36 +0300";
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease] ;
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease]];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setDateFormat: dateFormat ];
NSDate *newDate = [dateFormatter dateFromString: tmp_string];
NSLog(#"dateFromThatString: %#", newDate);
In just command line utility the result is same
"03/08/2011 10:07:36 +0300"
.
But in the project with gdata-objectivec-client linked to it, the result is changed to
"03/08/2011 07:07:36 +0000"
I cant find what's the problem, any suggestions?
Reading about this subject i've learned that "NSDate is not aware of time zones, it always stores dates in a time zone independent manner (as a span of time since a specific reference date)", so those two NSDate objects representing two different strings in two different projects are the same, there is just some problem in difference between description of NSDate objects, so.. it's not a big problem for future work, because i needed these description only for an easy debug. I will just not use description method, but [NSFormatted stringFromDate:].
It's interesting how gdata-objectivec-client influenced on a project, that description of nsdate obj returns same time, but responding to +0000 gmt offset.
But it's only for discussion.
It looks like the date formatter has different time zones in each case. You can change the time zone using -[NSDateFormatter setTimeZone:].

Objective-C and sqlite's DATETIME type

I have a sqlite3 table that I'm trying to map to an object in objective-C. One attribute of the table is 'completed_at' which is stored as a DATETIME.
I want to create a property on my objective-C class (which inherits from NSObject) that will map well to the 'completed_at' attribute.
Objective-C has an NSDate type but I'm not sure if that will map directly?
I am sharing here just the core things regarding date formatting for saving and retrieving the data for presentation. If you have any problem with this code snippet then I will share the full code that I used for my project.
When you save your data, bind your date value in the sql statement like this way:
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *dateString=[dateFormat stringFromDate:[NSDate date]];
sqlite3_bind_text(saveStmt, 1, [dateString UTF8String] , -1, SQLITE_TRANSIENT);
and when you retrieve data you have to write this code:
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *myDate =[dateFormat dateFromString:[NSString stringWithUTF8String:(char *)sqlite3_column_text(selectstmt, 1)]];
now you have a variable myDate of NSDate type which you can render in your way:
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd-MM-yyyy hh:mm:ss a"];
NSLog(#"My Date was : %#", [formatter stringFromDate:myDate]);
You must have to remember three things:
In your SQLite date field type should be DATETIME
Date format should be same when you store and when you retrieve
Now you can show in your own way but following the format. Below the format details is given.
Format:
'dd' = Day 01-31
'MM' = Month 01-12
'yyyy' = Year 2000
'HH' = Hour in 24 hour
'hh' = Hour in 12 hour
'mm' = Minute 00-59
'ss' = Second 00-59
'a' = AM / PM
I have zero experience with Objective-C, but I found Apple's NSDate Class Reference with a google search. With the information provided on the linked page you should be able to figure out how to manipulate 32-bit epoch times in Objective-C, and this would work well in SQLite. I would probably create the completed_at column as type INTEGER for 32-bit times.
SQLite really prefers Julian dates, which are floats. I haven't found any documentation explaining how one might coerce the NSDate class into working with Julians.
timeIntervalSince1970 looks very interesting.
This came up a couple of weeks ago:
Persisting Dates to SQLite3 in an iPhone Application
The formatter is important if you are trying to effect the presentation but if you use if for internal storage, you are defining a string which can defeat the DB-engine's ability to use the value for computation, comparison, sorting, etc. Also, if you are going to have different clients inserting the date value into the DB you would have to write conversion functions everywhere. I used the following and it worked as expected (schema's column defined as DATETIME):
dateExpires = [NSDate dateWithTimeIntervalSinceNow: sqlite3_column_double(queryStmt, 5)];
I inserted into the SQLITE3 db with the Firefox add-on as "4/12/2010" here in Central time zone. Viewing the value of 'dateExpires' in XCode-debugger displayed as:
2010-04-12 23:19:48 -0500
Sure enough, that is the correct time.
Also, to insert into the SQLITE DB you will put the value [NSDate date]