Objective C: Questions regarding time and array - objective-c

Q1. NSDate *now = [NSDate date];
NSLog(#"Current date:%#",now);
The time shown is 4 hours ahead of the system time. I am wondering why is it so and how can I correct it?
Q2.. In C/C++, strings are treated as an array of characters. Is it the case in Objective-C also?

NSDate *now = [NSDate date];
NSLog(#"Current date:%#",now);
it's "now" not "date".

A1: NSLog(#"%#", now) is effectively the same as NSLog(#"%#", [now description]). The NSDate object doesn't care what the timezone is, so its description method will just give you the time in UTC. If you need to format with the right timezone and locale, you'll need to use an NSDateFormatter object to convert it to a nicely formatted string first.
A2: Yes and no, but mostly no. You can do this:
char *cString = "I am a C string";
to create a C string, which you can treat exactly as you would in C. That's something you very rarely see in Objective-C, though, except when it's absolutely necessary. The "normal" way to use strings is with instances of NSString or NSMutableString, which are fully-fledged objects:
NSString *normalString = #"I'm above all that."; (note the # symbol)

Related

Why can't I store timeIntervalSince1970 return value to a pointer?

I'm a beginner (at programming) learning Objective-C. Xcode will not let me allocate a return value from an NSDate object; I am confused by this.
NSDate *now = [NSDate date];
long seconds = [now timeIntervalSince1970];
If I use a pointer to store the seconds:
long *seconds = [now timeIntervalSince1970];
I see the error:
Initializing 'long *' with an expression of incompatible type 'NSTimeInterval' (aka 'double')
Why can't I say "Give me the return value of now.timeIntervalSince1970 and stick at this address"?
Another question is, why can't I initialize an object and use it without needing a pointer? This will not work.
NSDate dateObject = [NSDate date];
I realize my question probably has a very easy answer, but the book doesn't explain any of the "why does it work this way" questions.
It's because [NSDate timeIntervalSince1970] returns the value as an NSTimeInterval which is a typedefd double, which is a primitive type, not an object type.
The value counts the number of seconds since 1-Jan-1970, and does not need to be an object.
You could store it in a pointer, but that would be silly:
NSTimeInterval *elapsed = (NSTimeInterval *)malloc(sizeof(NSTimeInterval));
*elapsed = [now timeIntervalSince1970];
...
free(elapsed);
timeIntervalSince1970 returns an NSTimeInterval which is a type def of double. In the Apple documentation you will see this defined like
objective-c
typedef double NSTimeInterval;
Swift
typealias NSTimeInterval = Double
and double is a primitive and not an object type so shouldn't be a pointer (Shouldn't is in Apple documentation). The Apple documentation also states the below
NSTimeInterval is always specified in seconds; it yields sub-millisecond precision over a range of 10,000 years.
Here is the Apple Documentation

cast message argument objective c

I've got an NSMutableDictionary (p) - via a JSON string - that has a bunch of dates in it (seconds since the epoch). I want to convert them to NSDate. I tried the following:
NSDate *created_dt = [NSDate dateWithTimeIntervalSince1970:[p objectForKey:#"created_dt"]];
But I get "sending 'id' to parameter of incompatible type 'NSTimeInveral' (aka 'double')"
What's the syntax that I am missing?
Thanks!
dateWithTimeIntervalSince1970: expects an NSTimeInterval (which typedefed to a double). You pass it an object (the return value of objectForKey). This object is probably an NSNumber, so you can simply solve your problem like this:
NSDate *created_dt = [NSDate dateWithTimeIntervalSince1970:[[p objectForKey:#"created_dt"] doubleValue]];

parsing date from string, nsdate, objective c

Below is a string represented a date
NSString *dateStr = #"2011-07-06";
And when I am trying to convert it to NSDate by doing :
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"yyyy-MM-dd"];
NSDate *tmp = [format dateFromString:dateStr];
NSLog(#"tmp is %#",[tmp description]);
What I am getting from the console is
tmp is 2011-07-06 04:00:00 +0000
I dont understand why I am getting extra info :04:00:00 +0000 for the result
Please help if you experienced it before
Your code
NSString *dateStr = #"2011-07-06";
NSDateFormatter *format = [[NSDateFormatter alloc] init];
[format setDateFormat:#"yyyy-MM-dd"];
NSDate *tmp = [format dateFromString:dateStr]
will result in a NSDate object, that represents your local time at 0:00 — the beginning of the day.
but if you print a plain date object, it will result in a string that represents the time in GMT timezone, as internally all dates are normalized to that timezone.
As your string is 4 hours ahead, we can tell, that you most likely are in East Europe, maybe Moscow.
So if you want to see the string in your timezone, you need to use a NSDateFormatter to create it from the date object.
NSLog(#"tmp is %#",[formatter stringFromDate:tmp]);
to check, if it is correct, what I said, change the format to contain the time components.
formatter.format = [#"yyyy-MM-dd HH:mm"];
NSLog(#"tmp is %#",[formatter stringFromDate:tmp]);
The formatter will also take "oddities" like Leap Day, Leap Month (yes — those exits), Daylight Saving Times, Leap Seconds … in account — accordantly to the current calendar.
A great WWDC 2011 Video: Performing Calendar Calculations — a must-see for every cocoa developer.
BTW: to print a object with NSLog you dont need to call -description on it to pass in a string. NSLog will do this internally.
NSLog(#"tmp is %#", tmp);
is just fine.
The answer is simple, NSLog just converts the NSDate to a NSString, using its formatter with GMT (zero) timezone.
Your formatter is by default set to your default time zone, which is probably -4:00. When you print it out, NSLog converts it to 0:00, adding 4 hours.
In general, it's unsafe to parse dates without specifying their timezone.

What's the simplest way to parse RFC3339 date string in iOS?

Youtube API returns date string in RFC3339 format. I found how to parse it on manual, anyway, this is too long.
- (NSString *)userVisibleDateTimeStringForRFC3339DateTimeString:(NSString *)rfc3339DateTimeString
// Returns a user-visible date time string that corresponds to the
// specified RFC 3339 date time string. Note that this does not handle
// all possible RFC 3339 date time strings, just one of the most common
// styles.
{
NSString * userVisibleDateTimeString;
NSDateFormatter * rfc3339DateFormatter;
NSLocale * enUSPOSIXLocale;
NSDate * date;
NSDateFormatter * userVisibleDateFormatter;
userVisibleDateTimeString = nil;
// Convert the RFC 3339 date time string to an NSDate.
rfc3339DateFormatter = [[[NSDateFormatter alloc] init] autorelease];
enUSPOSIXLocale = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"] autorelease];
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
if (date != nil) {
// Convert the NSDate to a user-visible date string.
userVisibleDateFormatter = [[[NSDateFormatter alloc] init] autorelease];
assert(userVisibleDateFormatter != nil);
[userVisibleDateFormatter setDateStyle:NSDateFormatterShortStyle];
[userVisibleDateFormatter setTimeStyle:NSDateFormatterShortStyle];
userVisibleDateTimeString = [userVisibleDateFormatter stringFromDate:date];
}
return userVisibleDateTimeString;
}
I can make a function contains this, but I want to know is there pre-defined way on Cocoa foundations or standard C or POSIX library to do this. And I want to use it if there it is. Can you let me know is there more simpler way? Or It will be very appreciate if you confirm this is most simple way :)
The pure stuff-that-comes-with-Cocoa way is exactly what you're doing. You can make this method both shorter and faster by creating the date formatters elsewhere, probably in init, and using/reusing them in this method.
You need two formats because fractional seconds are optional, and the timezone should be Z5, not Z. So you create two formatters with formats
#"yyyy'-'MM'-'dd'T'HH':'mm':'ssX5"
#"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSSSSSX5"
and try them both. That's obviously for RFC3339; your strings might not be in that format. Glad you didn't ask for RFC822 which is a pain to do correctly. But you should really have a method first that returns NSDate, because most uses don't actually need a string formatted for the user.
I had some problems with parsing RFC 3339 in Obj-c since the fractional seconds and zone seems to be optional.
The most reliable function that I found was this Gist (of which I am not the author): https://gist.github.com/mwaterfall/953664

2 NSDates that should be equal aren't?

I'm using the JSON library from Stig Brautaset(http://code.google.com/p/json-framework) and I need to serialize an NSDate. I was considering converting it into a string before JSONifying it, however, I ran into this weird behavior:
Why aren't these NSDates considered equal?
NSDate *d = [[NSDate alloc] init];
NSDate *dd = [NSDate dateWithString:[d description]];
NSLog(#"%#", d);
NSLog(#"%#", dd);
if( [d isEqualToDate:dd] ){
NSLog(#"Yay!");
}
When you describe the original date object you lose some sub-second precision from the original object — in other words, -description shaves off fractional seconds, and returns
A string representation of the receiver in the international format YYYY-MM-DD HH:MM:SS ±HHMM, where ±HHMM represents the time zone offset in hours and minutes from GMT
When you create a new date object based on the description, you get it in whole seconds because the string is only precise to a whole second. So -isEqualToDate: returns NO because there is a difference of a fraction of a second between your two date objects, which it's sensitive to.
This method detects sub-second differences between dates. If you want to compare dates with a less fine granularity, use timeIntervalSinceDate: to compare the two dates.
So you'd do something like this instead (NSTimeInterval measures in seconds):
if ([d timeIntervalSinceDate:dd] == 0) {
NSLog(#"Yay!");
}
isEqualToDate detects subseconds differences between dates, but the description method does not include subseconds.
Because they're not equivalent:
NSDate *d = [NSDate date];
NSDate *dd = [NSDate dateWithString:[d description]];
NSLog(#"%f", [d timeIntervalSinceReferenceDate]);
NSLog(#"%f", [dd timeIntervalSinceReferenceDate]);
Produces:
2011-04-28 11:58:11.873 EmptyFoundation[508:903] 325709891.867788
2011-04-28 11:58:11.874 EmptyFoundation[508:903] 325709891.000000
In other words, the +dateWithString: method does not maintain sub-second precision.