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

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

Related

iOS how to parse "MM/DD/YY HH:MM AM" using NSDateFormatter on a device with 24 hour clock? [duplicate]

I'm having a problem. I get incoming time strings in 12-hour format, and I'm turning them into NSDate objects. When the iPhone is in 12 hour format, no problem. But when it's in 24 Hour format, things go wrong. Here's some sample code to demonstrate:
NSString *theTime = #"3:19 PM";
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"h:mm a"]; // "3:19 PM"
NSDate *date = [formatter dateFromString:theTime];
NSString *theString = [formatter stringFromDate:date];
In 24 hour mode, date is 1970-01-01 03:19:00, and theString is "3:19" - WRONG
In 12 hour mode, date is 1970-01-01 15:19:00, and theString is "3:19 PM" - RIGHT
So... question 1: why is the device's 24 hour setting overriding my date formatter setting?
and more importantly, question 2: How do I get a proper conversion from 12 hour time to 24 hour time?
I already have code to detect if the phone is in 24 hour mode, but other than digging around in the string and swapping the 3 with a 15, there doesn't seem to be a clean way to do this.
Not sure if you still need it, but I've had a similar problem which got solved by setting the locale for the date formatter. That is, if you want to force it to 12-hour mode, regardless of the user's 24/12 hour mode setting, you should set the locale to en_US_POSIX.
The reason for this behaviour is Locale, set the correct Locale
NSString *strAgendaDate = #"01/17/2012 12:00 AM";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"] autorelease];
[dateFormatter setDateFormat:AgendaDateFormatForMeeting];
NSDate *meetingDate = [dateFormatter dateFromString:aStrDate];
[dateFormatter setDateFormat:AgendaDateRepresentation];
strAgendaDate = [dateFormatter stringFromDate:meetingDate];
It works for both 24-hour and 12 hour format
I believe the #"h:mm a" should be #"HH:mm a".
If you use the pre-build dateformatter in cocoa, everything will be taken care of for you.
NSDateFormatter *timeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[timeFormatter setTimeStyle:NSDateFormatterShortStyle];
[timeFormatter setDateStyle:NSDateFormatterNoStyle];
NSDateFormatterShortStyle and NSDateFormatterNoStyle comes in different varieties.
Using those will make sure you respect the settings the user has selected for dates and times.
The 12-14 hour clock conversion is taken care of by the SDK, if you have a model or some value object for storing your dates try to keep them as NSDate. This way you can format them only when you need to display them. Saving dates as strings could open a world of trouble when you maybe parse them from xml where the GMT is specified separately or try to add and subtract NSTimeIntervals.
I changed from #"hh:mm:ss" to #"HH:mm:ss" and time style was changed from "1:03 PM" to "13:03".
Hope this will help you.
Okay, I left a comment, but it squished all the code together, so I'll have to "answer" my question with a comment:
Thanks. I gave it a whirl with this code:
NSString *theTime = #"3:19 PM";
NSDateFormatter *timeFormatter = [[[NSDateFormatter alloc] init] autorelease];
[timeFormatter setTimeStyle:NSDateFormatterShortStyle];
[timeFormatter setDateStyle:NSDateFormatterNoStyle];
NSDate *date = [timeFormatter dateFromString:theTime];
NSString *theString = [timeFormatter stringFromDate:date];
And date comes up nil. I ran into this earlier when I tried this route, and it's not working. Very frustrating.

Lossless conversion of NSDate to NSString and vice versa

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.

SQLite storing, retrieving and comparing a DATETIME field

I am really stuck trying to compare dates in SQLite queries in Objective C. Here's what I'm doing:
Storing the date:
This document tells me to use the dateformat specified below, but it doesn't seem right. I tried using yyyy-MM-dd hh:mm:ss without success too though.
NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"YYYY-MM-DD HH:MM:SS"];
NSString *dateString=[dateFormat stringFromDate:today];
NSString *query = [NSString stringWithFormat: #"INSERT INTO user (edited) VALUES (\"%#\")", dateString];
Comparing the date
I am using a timestamp for this, which I convert to a datetime string
long stamp = [sessie integerForKey:#"stamp"];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"YYYY-MM-DD HH:MM:SS"];
NSString *dateString = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970:stamp]];
sqlite3_stmt *result = [db selectQuery:[NSString stringWithFormat:#"SELECT * FROM user WHERE edited > '%#'", dateString]];
The timestamp is simply generated using [[NSDate date] timeIntervalSince1970]. The problem is that the query won't give the correct results, and I don't even know if the date is stored correctly in the first place. Any help would be greatly appreciated!
A couple of observations:
For your date string, you do definitely do not want to use YYYY-MM-DD HH:MM:SS. That will not generate a valid date string. Using yyyy-MM-dd hh:mm:ss is much closer, but not quite right, either (since you'll use 12-hour hh). Use yyyy-MM-dd HH:mm:ss instead.
This date format, though, does not capture time zone, so, if you store date strings in your SQLite database, you should use UTC (GMT) as discussed in the SQLite Date And Time Functions documentation.
NSDateFormatter * formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ss"];
formatter.timeZone = [NSTimeZone timeZoneWithName:#"UTC"];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"en_US_POSIX"];
NSString *dateString = [formatter stringFromDate:today];
As shown above, you probably want to also specify the locale so that the format of the dates will not change depending upon the localization settings of the device.
Note that you might consider using timeIntervalSince1970 to store the date as a REAL value in your database (as opposed to a TEXT). This can be a little more efficient and automatically addresses the UTC issue.

iOS how to show a date in another language,

I've recently been working on a French version of our application use NSLocalizedStrings, so far everything is working great.
But my problem now is with dates. I show dates often in my application, in different formats, depending on the situation.
Ex:
-Fri Feb 22, 2013
-Monday February 18, 2013
-Feb 18
-Dec 5, 2012
The thing is, dates in French are not only different in terms of the name of the month, but also the order in which month, day and year appear.
Ex:
-Dec 5, 2012 would be 5 Dec 2012
-Monday February 18, 2013 would be Lundi le 18 Fevrier 2013.
I have the individual month/day names in my Localizable.string files, but how do I manage the order of how it's displayed.
Should I have an if statement that checks the current device language like so?:
NSString *currentLanguage = [[NSLocale preferredLanguages] objectAtIndex:0];
if([currentLanguage isEqualToString:#"fr"])
{
//Handle French logic
}
This is probably not the best way to go about it.
Any ideas?
You should use an NSDateFormatter and feed it your desired NSLocale like this:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"nl_NL"];
dateFormatter.dateFormat = #"EEEE d MMMM yyyy HH:mm";
'EEEE' is the full name of the day of the week, which, in my case, will be displayed in dutch.
Use NSDateFormatter. For example:
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
NSDate *date = [NSDate dateWithTimeIntervalSinceReferenceDate:162000];
NSString *formattedDateString = [dateFormatter stringFromDate:date];
NSLog(#"formattedDateString: %#", formattedDateString);
NSDateFormatterMediumStyle will automatically format the date according to the user's preference (English, French, etc.).
If you need custom styling and the app runs in iOS 4.0+, you can use a custom template in your date formatter:
NSString *formatString = [NSDateFormatter dateFormatFromTemplate:#"EdMMMyyy" options:0
locale:[NSLocale currentLocale]];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:formatString];
NSString *todayString = [dateFormatter stringFromDate:[NSDate date]];
NSLog(#"todayString: %#", todayString);
Set proper locale to your NSDateFormatter when you convert date to string, then date formatter will handle all specifics of formats depending on user settings for you:
NSDateFormatter *formatter = ... // Create and setup formatter
[formatter setLocale:[NSLocale autoupdatingCurrentLocale]];
// Now you can convert date to string
That's probaly much easier:
There is something which is called
NSDateFormatterShortStyle,
NSDateFormatterMediumStyle
NSDateFormatterLongStyle
Set Date and Time components individually:
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
Ios will format that correctly if the language of the user.
Further look at Data Formatting Guide
I would try the class function localizedStringFromDate:dateStyle:timeStyle: in NSDateFormatter before attempting to use the NSDateFormatter and a template (from Unicode Technical Standard #35):
Example:
[NSDateFormatter localizedStringFromDate:dateTime dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterNoStyle];
You can select both different date and time outputs of various lengths detailed in the docs.
Apple documentation: NSDateFormatter localizedStringFromDate:dateStyle:timeStyle:

iOS: Display time in 24-hour format if locale appropriate?

My iOS app is going to display the current time. How do I determine whether the user's locale prefers 12-hour or 24-hour time formatting?
See the NSDateFormatter class http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSDateFormatter_Class/Reference/Reference.html
Something like:
NSDate *date = [NSDate date];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterNoStyle];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[dateFormatter setLocale:[NSLocale currentLocale]];
NSString *dateString = [dateFormatter stringFromDate:date];
You generally don't need to figure out what the user's locale settings are when displaying data to the user. If you use the relevant NSFormatter (NSNumberFormatter for numbers and NSDateFormatter for date and time, which is the relevant one for you in this case), then you will automatically get the output that is appropriate to the user's locale... i.e what Apple calls the "localized representation of the object"

Resources