Copying an NSDate (wanting independent objects) - objective-c

NSDate conforms to NSCopying protocol. According to the documentation for NSCopying protocol:
a copy must be a functionally independent object with values identical
to the original at the time the copy was made.
But, when I do this:
NSDate *date1 = [NSDate date];
NSDate *date2 = [date1 copy];
NSLog(#"result: date1 0x%x date2 0x%x", (int)date1, (int)date2);
// "result: date1 0x2facb0 date2 0x2facb0"
The two objects are identical (same object id). What am I missing? How do I get an independent object as a copy?

copy does not guarantee different object pointer. “Functionally independent” means that changes to the original object will not be reflected in the copy, and thus for immutable objects copy may work as retain (I don't know if this is guaranteed though, probably not).
Try date2 = [[NSDate alloc] initWithTimeInterval:0 sinceDate:date1].

Beware!
I recently found out, that on iOS 8.1(.0) [NSDate dateWithTimeInterval:0 sinceDate:date1] returns date1! Even the alloc/init returns the same object.
The deep-copy was important for me, as I create copies of objects. Later I compare the timestamps with [date1 laterDate:date2] == date2 which will always be true, if the deep-copy doesn't work.
Same for [date1 dateByAddingTimeInterval:0]
I have no good solution for iOS 8.1, yet, but keep searching and will update here. An emergency-workaround could be to create a date-string with a formatter, and then create a date from the string with the same formatter.
Edit: It get's even worse:
NSString *date1String = [iso8601DateFormatter stringFromDate:date1];
date2 = [iso8601DateFormatter dateFromString:date1String];
(lldb) p date1
(NSDate *) $0 = 0xe41ba06fd0000000 2014-11-03 01:00:00 CET
(lldb) p date2
(NSDate *) $1 = 0xe41ba06fd0000000 2014-11-03 01:00:00 CET

NSDate is abstract class. It often uses 'tagged pointer' trick so it will impossible to get different objects for same dates in most cases. Real class name NSTaggedDate in this case.
Some dates could be real object (if there is no space in tagged pointer for it). Ex:
[[NSDate alloc] initWithTimeIntervalSinceReferenceDate:1234567890123456]

Related

The lifetime of instance of object in ObjC

I'm still little bit perplexed by pointers and memory management (starting out with ObjC and Cocoa). What got me thinking, is this piece of code:
double seconds = [[NSDate date] timeIntervalSince1970];
This is what I understand:
I get value of float type returned from calling method/message on NSDate class
This value gets stored in variable seconds
What I don't understand is am I creating NSDate object (=instance of class NSDate) at all? Is this object only temporary?
I always thought that the way to create an object and have the object to be persistent (at least until ARC steps in or it is destroyed when function ends) is to create a pointer to it. Maybe like this:
NSDate *now = [NSDate date];
[now timeIntervalSince1970] // get the value
Does this mean that in my original example, there is some unnamed (no variable pointing to it) instance of NSDate created on the heap and once it returns the float value it gets removed from heap?
Does this mean that in my original example, there is some unnamed (no
variable pointing to it) instance of NSDate created on the heap and
once it returns the float value it gets removed from heap?
Yes, that's exactly right.
ARC will remove the object when it goes out of scope.
Even before ARC, the object would be created as autorelease and be released the next time through the event loop (or whenever the nearest autorelease pool was drained).

Difference between dot syntax and valueForKey

Please note that update 3 is probably most relevant
Im setting a NSTimeInterval property of a managed object with an nsdate object using setValue:forKey:
When i attempt to get the value I get weird stuff, at runtime this
NSLog(#"[managedObject valueForKey:#\"startTime\"] : %#, [NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]: %#",
[managedObject valueForKey:#"startTime"],[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]);
Returns
[managedObject valueForKey:#"startTime"] : 2012-07-14 08:13:05 +0000,
[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]]: 1981-07-14 08:13:05 +0000
Update 1
The value returned by [managedObject valueForKey:#"startTime"] is correct. However I would prefer to use [NSDate dateWithTimeIntervalSince1970:[managedObject startTime]] or something similar so that it is more strongly typed.
I believe [managedObject startTime] returns an incorrect value => 363954111.000000 .
However i set it with something like this:
managedObject setValue:1342261311 forKey:#"startTime"
It is worth noting that I am unsure whether this is incorrect because [managedObject valueForKey:#"startTime"] returns a correct NSDate object.
Update 2
I've logged the double values returned by KVC and . syntax.
managedObject.startTime = 363954111.000000
valueForKey timeIntervalSince1970 = 1342261311.000000
Update 3
Okay, I've set up a test, start time is set like this entity.startTime = [[NSDate dateWithTimeIntervalSince1970:1342261311] timeIntervalSince1970]; and end time is set like this [entity setValue:[NSDate dateWithTimeIntervalSince1970:1342261311] forKey:#"endTime"];
When i write them to log i get this start = 1342261311.000000, end = 363954111.000000
It seems that the NSDate object is being unwrapped incorrectly, has anyone seen this before?
The problem here is that valueForKey: is intended to be used with object values, in fact it returns an id.
As a convenience, valueForKey: wraps primitive types (such as integers and doubles) in their NSNumber counterparts.
The reason you see two different values is that valueForKey: returns an id, which essentially is a pointer to the position in memory where the NSNumber happens to be stored. Your code then just takes this arbitrary memory address and somehow interprets it as a double and then constructs an NSDate out of that.
Calling the startTime accessor method directly, on the other hand, returns the double without any further ado.
If you want to use valueForKey:, you can do something like this to get the real value:
NSTimeInterval tiv = [[managedObject valueForKey:#"startTime"] doubleValue];
and then work from there.
I am actually a bit surprised that the compiler doesn't emit a warning about this. Apple's latest compilers have become quite adept at catching problems like this one.
It was a problem with the difference in epochs. NSDate uses Jan 1 2001 as an epoch. So when I was getting the value I was using the unix epoch (1970). That gave me a difference in values.
When KVC unwraps and wraps NSTimeInterval with a NSDate object it uses the NSDate 2001 epoch.
So instead of using dateWithTimeIntervalSince1970
I used dateWithTimeIntervalSinceReferenceDate when getting the value.
NSTimeInterval is a typdef of a double
typedef double NSTimeInterval;
You can not store scalars directly in core data but you either have to wrap them in a NSNumber or in your case it may be easier to use a NSDate.
If startTime is a NSTimeInterval (and not an NSDate), you are comparing two different things there, a double and an NSDAte object.
[managedObject valueForKey:#"startTime"] will return you an NSTimeInterval, a primitive (which you should print with %f by the way).
[NSDate dateWithTimeIntervalSince1970:[managedObject startTime]] will return you a NSDate.
If you really want to comare the two, you should use [NSDate dateWithTimeIntervalSince1970:[managedObject valueForKey:#"startTime"]] to properly compare two NSDate objects.

NSDateFormatter dateFromString returns incorrect date [duplicate]

This question already has answers here:
Get NSDate from NSDate adjusted with timezone
(2 answers)
Closed 9 years ago.
I am trying to use NSDateFormatter in my app which takes a date string and formats it to an NSDate so that I can do Date Comparisons, however I am finding when I use dateFromString and format it the date is losing one day.
NSString *dateString = #"02-06-2012";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy"];
NSDate *dateFromString = [[NSDate alloc] init];
dateFromString = [dateFormatter dateFromString:dateString];
NSLog(#"My Date = %#", dateFromString);
[dateFormatter release];
This outputs to the console:
My Date = 2012-06-01 23:00:00 +0000
Try adding this lines to your code,
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"GMT+0:00"]];
or
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithName:#"GMT"]];
SWIFT update :
Code from quetion,
let dateString = "02-06-2012"
var dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "dd-MM-yyyy"
var dateFromString : NSDate = dateFormatter.dateFromString(dateString)!
println("My Date \(dateFromString)")
And Solution ,
dateFormatter.timeZone = NSTimeZone(name: "GMT")
OR
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT+0:00")
I don't believe that Dhruv's answer is correct. In fact, it's not clear there's any problem at all. You just seem to have an incorrect expectation of what should happen and/or interpretation of what's happening.
NSDate represents a moment in time. This moment does not have one unique name. It will be known by different names in different places and under different naming systems (time zones, calendars). NSDate doesn't deal with any of this, except lamely in its -description method, where it has to produce a string representation of that moment.
Second, a string like "02-06-2012" doesn't specify a precise moment in time. First of all, it's just a date with no time information, so NSDateFormatter just defaults to the first moment for that date. Second, it doesn't specify the time zone. The first moment of the calendar day is a different moment in each time zone. Unless you specify a time zone with -setTimeZone: or the string itself carries time zone information, NSDateFormatter assumes that any date strings you ask it to parse are in the current time zone.
So, your dateFromString object represents the first moment of the specified date, 02-06-2012, in your time zone. I expect this is what you wanted. However, you then got confused by the way that NSDate describes itself when logged. As I said, NSDate has to pick some "name" (string representation) for the moment it represents and which name it picks is fairly arbitrary. These days it is picking the name that the moment is known by in UTC. I gather from the log output shown in your question that you are located at UTC+0100. So, the date may look like it's one day earlier but it really is the same moment you specified. In other words, "2012-06-01 23:00:00 +0000" and "2012-06-02 00:00:00 +0100" are two equivalent names for exactly the same moment in time. You just aren't used to seeing the first one and misinterpreted it.
The lesson is that you have to stop relying on NSDate's self-description to be in any particular time zone. Really, you have to not rely on anything about it, since it's not documented. In fact, the docs for -[NSDate description] state, "The representation is not guaranteed to remain constant across different releases of the operating system."
Dhruv's solution seems to help merely because it causes NSDateFormatter and -[NSDate description] to agree on the time zone. But that's unreliable. It wouldn't work on Snow Leopard, for example, because -[NSDate description] used the local time zone instead of UTC in that version of the frameworks.
More importantly, though, it alters the actual moment represented by the NSDate object you get from NSDateFormatter's interpretation of your date string. I suspect you really want that to have a specific meaning – you want the string to be interpreted as being in the local time zone – and his solution thwarts your intent.
tl;dr: you were getting the date you wanted all along; don't rely on -[NSDate description]; don't use Dhruv's solution

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);

Should I send retain or autorelease before returning objects?

I thought I was doing the right thing here but I get several warnings from the Build and Analyze so now I'm not so sure. My assumption is (a) that an object I get from a function (dateFromComponents: in this case) is already set for autorelease and (b) that what I return from a function should be set for autorelease. Therefore I don't need to send autorelease or retain to the result of the dateFromComponents: before I return it to the caller. Is that right?
As a side note, if I rename my function from newTimeFromDate: to gnuTimeFromDate the analyzer does not give any warnings on this function. Is it the convention that all "new*" methods return a retained rather than autoreleased object?
In Memory Management Programming Guide for Cocoa it says "A received object is normally guaranteed to remain valid within the method it was received" and that "That method may also safely return the object to its invoker." Which leads me to believe my code is correct.
However, in Memory Management in Cocoa it says "Assume that objects obtained by any other method have a retain count of 1 and reside in the autorelease pool. If you want to keep it beyond the current scope of execution, then you must retain it." Which leads me to think I need to do a retain before returning the NSDate object.
I'm developing with Xcode 3.2.1 on 10.6.2 targeting the iPhone SDK 3.1.2.
screenshot of build/analyze output http://nextsprinter.mggm.net/Screen%20shot%202009-11-15%20at%2008.33.00.png
Here's the code in case you have trouble reading the screen shot:
//============================================================================
// Given a date/time, returns NSDate for the specified time on that same day
//============================================================================
+(NSDate*) newTimeFromDate:(NSDate*)fromDate
Hour:(NSInteger)hour
Minute:(NSInteger)min
Second:(NSInteger)sec
{
NSCalendar* curCalendar = [NSCalendar currentCalendar];
const unsigned units = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
NSDateComponents* comps = [curCalendar components:units fromDate:fromDate];
[comps setHour: hour];
[comps setMinute: min];
[comps setSecond: sec];
return [curCalendar dateFromComponents:comps];
}
You are nearly right. The only problem that clang correctly points out is that your method promises a retain count +1 object (for its name containing “new”) but you are returning an autoreleased object.
You have two options: removing the “new" from the method name or retaining the returned object. It’s far more Cocoa-ish to return the autoreleased object (as you do) and name the method timeFromDate:.