Lossless conversion of NSDate to NSString and vice versa - ios

I have an NSDate:
2015-07-13 16:04:01 +0000
and I want to convert it to an NSString to be able to store it on a server and then read it back from server and convert back to NSDate without losing any details.
I've been looking at other posts that have suggested doing it as follows:
NSDateFormatter *dateFormatter = [NSDateFormatter new];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSString *dateString = [dateFormatter stringFromDate:date];
and vice-versa:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"dd-MM-yyyy"];
NSDate *dateFromString = [dateFormatter dateFromString:message[#"date"]];
but this leads to the loss and compromise of the format:
Jul 13, 2015
How do I convert NSDate to NSString and back, eventually getting back the exact original NSDate?

You're only formatting the original NSDate with a date (not time) style that reflects the user's preferences and you're only formatting the retrieved NSDate with day, month, and year, i.e. "dd-MM-yyyy". You should be consistent your NSDateFormatters in order to maintain the original format.
Change both
[dateFormatter setDateFormat:#"dd-MM-yyyy"];
and
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
to
[dateFormatter setDateFormat:#"yyyy-MM-dd HH:mm:ss Z"];
to get and keep the entire date and time format.

An NSDate stores the number of seconds since a reference date. If you are using only iOS applications. then you call a method returning that number of seconds as an NSTimeInterval which is a number, and then you store the number. This means there is absolutely no loss of information. Your data will come back with better than microsecond precision.
If the data has to be read by different devices not running iOS then there is another method returning the number of seconds since Jan 1st, 1970. This is a very standard format that any OS should be able to handle.
Storing a number instead of a string seems to be much better.

Related

iOS Date 12/24 hour formatting (Objective C)

I need to convert a 12 Hour time format to 24 Hour format. Specifically the PM part of a date, as the API I communicate with only accepts 24-hour format.
Example:
02:00 PM needs to be converted to 14:00.
08:30 PM needs to be converted to 20:30.
I've tried several approaches and probably been close, but I can't seem to get it quite right.
For that you need to use NSDateFormatter and convert the time format to 24 Hours format.
NSString *time12Hours = #"02:00 PM";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"hh:mm a"];
NSDate *date = [formatter dateFromString:time12Hours];
[formatter setDateFormat:#"HH:mm"];
NSString *time24Hours = [formatter stringFromDate:date];
iPhone format strings are in Unicode format. Behind the link is a table explaining what all the letters above mean so you can build your own.
You can try this website for find your format string nsdateformatter.com whit NSDateFormatter

Convert 12 hr to 24

I'm converting 12 hour date to 24 here, but it failed to get perfect time.
Here is code :
NSString *dateStr = #"2016-08-12T04:10:14.915Z";
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd'T'hh:mm:ss.SSS'Z'"];
[dateFormat setTimeZone:[NSTimeZone localTimeZone]];
NSDate *date = [dateFormat dateFromString:dateStr];
and my final date is
2016-08-11 22:40:14 +0000
How ?
The NSDate object that you are getting is GMT. In France GMT -2, if I run your code I have a time of 02:10.
Am I correct assuming your GMT offset is -5:30 ?
NSDate objects don't have time zones; they represent an absolute
moment in time. However, when you ask one for its description (by
printing it in an NSLog, e.g.), it has to pick a time zone. The most
reasonable "default" choice is GMT. If you're not in GMT yourself, the
date will seem to be incorrect, by the amount of your own offset.
You should always use an NSDateFormatter, setting its timezone to
yours, before displaying a date.
Don't trust what NSLog or the debbuger are telling you about a NSDate.
use
NSString dateAsString = [dateFormat stringFromDate:date];
to check your date :)

Read UTC in ios and subtract to date

I get fro server dateTime (deadline) string like "2013-03-29 04:12:35" and I need to make stopwatch which is going to show in label estimated time to deadline in format hours::minutes:seconds. I thought to convert deadline to utc and then read from phone current utc, subtract and convert to format hours::minutes:seconds. How to read UTC in ios and convert string to UTC ( Is there easier way to accomplish this task) ?
Try this
NSString *utcDateString = #"2013-03-29 04:12:35";
NSDateFormatter *formatter = [[NSDateFormatter alloc]init];
//DateFormatter by default will be in local timezone.
[formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[formatter setFormat:#"yyyy-MM-dd HH:mm:ss"];
NSDate *utcDate = [formatter dateFromString:utcDateString];
NSDate *currentDate = [NSDate date];
Now you can use to find the date components(hour,minutes,seconds) using the method mentioned in this post.

Ensure AM / PM (Period) does not appear in NSDateFormat stringFromDate

The Unicode Date Format Patterns guide (here) state that appending an 'a' to the format will get the period (AM or PM for instance), e.g.,
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss a"];
However I wish to ensure that the period information does not appear but I cannot find a format string to do that. The format string I am using is as follows:
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
unfortunately, when I use stringFromDate I get the following:
2013-01-09T11:11:00 AM
I dont wish to simply strip AM or PM from the resultant string because the period syntax may be different in differing Calendars etc, I just want to stop the period information appearing.
----8<------
Consider the following code
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSString *stringDate = [formatter stringFromDate:[NSDate date]];
self.labelOutput.text = stringDate;
[formatter release];
This code will produce a string in the format I want - however I cannot use it for memory management reasons. The app I am working on is plagued by NSDateFormatter memory leaks. So we use a singleton class to provide a set number NSDateFormatters to the app which are never released and therefore we minimise how much memory is being leaked. Unfortunately these static NSDateFormatters are appending AM / PM even when I apply a date format string thus:
NSDateFormatter *formatter = [MyDateFormatter dateFormat:kFormatDateMediumStyleTimeShortStyle];
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
According to date formatter different output on different devices running same iOS version, set the local of your NSDateFormatter to en_US_POSIX will fix this.
Additional to set Local you may wish avoid problems with time zone setting it like:
[formatter setTimeZone:[NSTimeZone timeZoneWithAbbreviation:#"UTC"]];
It actually depends on user's settings.
Please see Fixed Formats part of Data Formatting Guide. Note this sentence:
In iOS, the user can override the default AM/PM versus 24-hour time
setting. This may cause NSDateFormatter to rewrite the format string
you set.
And at the end of the paragraph:
The representation of the time may be 13:00. In iOS, however, if the
user has switched 24-Hour Time to Off, the time may be 1:00 pm.
You need to use POSIX here a sample code
NSDateFormatter *rfc3339DateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
[rfc3339DateFormatter setDateFormat:#"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
// Convert the RFC 3339 date time string to an NSDate.
NSDate *date = [rfc3339DateFormatter dateFromString:rfc3339DateTimeString];
One option is to create your own NSFormatter. I did that on my code when I couldn't get the formatter in Xcode's Interface Builder to do what I wanted. Granted, I was only looking for hours, minutes, and seconds and not the full date.
As for the memory leaks: if you can, use ARC. If you can't, use Xcode's Static Analyzer to try and track down improper retain counts.
Here is the answer I have settled on. It's a bit simplistic but it does the job.
#interface NSDateFormatter (Utils)
- (NSString *)stringWithNoPeriodInformationFromDate:(NSDate*)date;
#end
#implementation NSDateFormatter (Utils)
- (NSString *)stringWithNoPeriodInformationFromDate:(NSDate*)date
{
NSString *stringWithPotentialPeriodInformation = [self stringFromDate:date];
NSString *stringWithNoAMInformation = [stringWithPotentialPeriodInformation stringByReplacingOccurrencesOfString:[self AMSymbol] withString:#""];
NSString *stringWithNoPeriodInformation = [stringWithNoAMInformation stringByReplacingOccurrencesOfString:[self PMSymbol] withString:#""];
return stringWithNoPeriodInformation;
}
#end

Deviation from expected output with NSString to NSDate conversion using NSDateFormatter

I am using the below method to convert a NSString to NSDate.
Always when I construct the NSDate from String, the date is one day behind the current day I have provided as part of the input and hour is 18:30:00 +0000. Why this deviation from what I have provided. I was expecting to have the same date what I have provided and hour as 00:00:00 +0000
+(NSDate*)convertStringToNSDate:(NSString*)string withFormat:(NSString*)format{
// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
//[dateFormat setTimeZone:[NSTimeZone localTimeZone]];
[dateFormat setDateFormat:format];
NSDate *date = [dateFormat dateFromString:string];
[dateFormat release];
return date;
}
This question comes up quite regularly but I could not find a suitable duplicate (searching on the phone does not help).
NSDate represents a specific point in time. When you log the value of an NSDate it is displayed in GMT, which is 5.5 hours behind your timezone (India, I assume). So the value is correct. If you run that date back through your date formatter you will get the local time of midnight again, since the date formatter is using your local time zone.

Resources