NSDateFormatter dateFromString returns incorrect date [duplicate] - objective-c

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

Related

Objective-c NSDateFormatter timezone reverts to GMT

I am playing about with some timezone calculations and I have noticed quite an annoying error which is causing me problems.
If I create a date, use NSDateformatter to convert it to a certain time zone, retrieve that string, and then use dateformatter to convert the string back into a date object, it keeps reverting to my local GMT time.
Example
NSDate *date = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss zzz"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"COT"]];
NSString *str = [dateFormatter stringFromDate:date];
NSLog(#"%#", str);
//prints 2015-02-04 10:33:45 GMT-5
NSDate *newDate = [dateFormatter dateFromString:str];
NSLog(#"%#", newDate);
//Prints 2015-02-04 15:33:45 +0000
Why does it keep reverting back to GMT? I need that date object to accurately reflect the time zone I have set the dateformatter to for some testing purposes, so this is quite a frustrating issue.
Any help would be much appreciated
I believe that the problem is that NSDate itself (i.e. the value you're logging at the end) doesn't have a time zone - it's just a moment in time. You're specifying the time zone *when formatting the value using stringFromDate*, and you're still using that when you *parse* the value back to anNSDate... but theNSDate` value itself doesn't remember the time zone.
To give a different example, imagine you had an IntegerFormatter for NSInteger, which let you say whether you wanted to format and parse in hex or decimal. You could format the decimal value 16 to 0x10, and then parse that value back... but the NSInteger wouldn't "remember" that it was parsed from hex. It's exactly the same here - the time zone plays a part in the parsing (at least when the value itself doesn't specify the time zone) but it isn't part of the result in itself.
I need that date object to accurately reflect the time zone
Then you need to keep the time zone separately alongside the NSDate, basically... (Looking at the documentation, it sounds like NSCalendarDate did what you want, but that's deprecated.)

NSDateformatter Converting date to string

I am having the following date and time 2013-04-25 10:42:44 +0000. When i convert the above date to string, i am getting the output as 2013-04-25 16:12:44. Following is the code i am using to convert the date to string
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc]init];
dateFormatter.timeZone = [NSTimeZone systemTimeZone];
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
NSString *dateStr= [dateFormatter stringFromDate:date]];
NSLog(#"dateStr--%#",dateStr);
This gets asked over and over (but is difficult to find via search).
When you log an NSDate object, you will always get the date in UTC format. This is how the description method of NSDate is currently implemented.
As long as the difference you are seeing can be accounted for based on your local timezone relative to UTC, then what you are seeing it correct and expected behavior.
BTW - there is no reason to set the date formatter's timezone to the "system" timezone. This is already done by default. Same for the locale. Only set the timezone or locale if you want something different from the current values.
I guess it's a timezone issue. Set your formatters time zone to GMT:
dateFormatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
I assume the variable date gets initialized via [NSDate date].
Maybe the quite extensive answer in the follwing topic is helpful here, too.
Does [NSDate date] return the local date and time?

NSDateFormatter dateFromString produces nil after first call in same method

Before Xcode 4 I used to use NSDate initFromString but it is now deprecated and produces errors in Xcode 4.2. So I jumped over to using NSDateFormatter dateFromString but ran into an issue in a method I call that gets a sunrise date from string and a sunset date from string to determine if it is day or night (so same method). The first call works fine, but the second call returns nil. I decided to see if it was repeatable so I created the following simple method and dumped it into another project in an innocuous place:
- (void)testDateFormatter
{
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat: #"yyyy-MM-dd hh:mm:ss Z"];
NSString *srDateTimeString = #"2011-11-09 07:08:00 -0800";
NSString *ssDateTimeString = #"2011-11-09 17:08:00 -0800";
NSDate *sunriseDateTime = [formatter dateFromString: srDateTimeString];
NSDate *sunsetDateTime = [formatter dateFromString: ssDateTimeString];
return;
}
The first call returns the correct date. The second call returns nil. I have tried several variations (such as creating two separate NSDateFormatters, preinitializing the dates, preinitializing the dates to the current time and then reassigning) but none do the expected thing.
Does anyone know what is going on here? Since I hadn't found any reference to this error anywhere I submitted it to Apple (bug #10420498).
Jack
In the second string, the hour is 17 (24-hour format) but the format string uses hh (lowercase) which is for 12-hour format.
Change the format string to yyyy-MM-dd HH:mm:ss Z.
In the documentation, see Date Formatters which contains a link to the Unicode Date Format Patterns listing each format specifier.

NSDate is 5 hours off

I run the following code:
NSDate *now = [NSDate date];
NSLog(#"now: %#", now);
and get :
2011-09-16 16:14:16.434 iSavemore[1229:7907] now: 2011-09-16 21:14:16 +0000
As you can see i'm running this at 16:14:16 (4:14 pm) but NSDate is returning 21:16:16 (9:14 pm!). Is this an Xcode4 issue or NSDate issue?
NSDate defaults to the Universal timezone (aka GMT).
I'm guessing you're somewhere on the East Coast, 5 hours behind UTC.
Try adding this to your date formatter...
NSDateFormatter *dateFormatter = [[[NSDateFormatter alloc] init] autorelease];
[dateFormatter setLocale:[NSLocale currentLocale]];
...and you should see your local time.
If you want to use a specified locale, rather than 'currentLocale', create a NSLocale for the relevant locale.
NSLocale *usLoc = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US"];
[dateFormatter setLocale:usLoc];
...actually that's US (so possibly not Central).
More specific timezone help can be found here...
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/Reference/Reference.html
However, if you want to show expiry time, wouldn't you still want it in the user's currentLocale?
If you look at the output you'll see that the log includes the timezone:
2011-09-16 16:14:16.434 iSavemore[1229:7907] now: 2011-09-16 21:14:16 +0000
^^^^^^
The time stamp of your log is local time. I assume you're in a timezone that is 5 hours ahead of UTC.
A NSDate refers to a particular point in time. It's up to you to display this however you want; usually with an NSDateFormatter.
This is the reason why you'll see plenty of recommendations against storing a time, or a date as anything other than an NSDate. If you try and store it as a string you'll run into a lot of trouble later on when trying to handle the display in different timezones.
Try setting the time-zone of your NSDate to one that is fitting your need, for example [NSTimeZone localTimeZone]
Just a wild guess here, but maybe it has something to do with time zones?

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]