NSDateFormatter has a lovely support for producing relative dates such as "Today", "Tomorrow", "Yesterday" when these are supported by the current language. A great advantage is that all of these are localised for you already – you don't need to translate the strings.
You can turn on this functionality with:
[dateFormatter setDoesRelativeDateFormatting: YES];
The down side is that this only seems to work for an instance that is using one of the predefined formats, such as:
[dateFormatter setDateStyle: NSDateFormatterShortStyle];
If you set up the date formatter to use a custom format, like this:
[dateFormatter setDateStyle: @"EEEE"];
Then when you call:
[dateFormatter stringFromDate: date];
… you will just get back an empty string.
I'd like to be able to get relative strings when possible, and use my own custom format when it is not.
I set up a category on NSDateFormatter to provide a workaround for this. An explanation follows the code.
In NSDateFormatter+RelativeDateFormat.h:
@interface NSDateFormatter (RelativeDateFormat)
-(NSString*) relativeStringFromDateIfPossible:(NSDate *)date;
@end
In NSDateFormatter+RelativeDateFormat.m:
@implementation NSDateFormatter (RelativeDateFormat)
-(NSString*) relativeStringFromDateIfPossible:(NSDate *)date
{
static NSDateFormatter *relativeFormatter;
static NSDateFormatter *absoluteFormatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
const NSDateFormatterStyle arbitraryStyle = NSDateFormatterShortStyle;
relativeFormatter = [[NSDateFormatter alloc] init];
[relativeFormatter setDateStyle: arbitraryStyle];
[relativeFormatter setTimeStyle: NSDateFormatterNoStyle];
[relativeFormatter setDoesRelativeDateFormatting: YES];
absoluteFormatter = [[NSDateFormatter alloc] init];
[absoluteFormatter setDateStyle: arbitraryStyle];
[absoluteFormatter setTimeStyle: NSDateFormatterNoStyle];
[absoluteFormatter setDoesRelativeDateFormatting: NO];
});
NSLocale *const locale = [self locale];
if([relativeFormatter locale] != locale)
{
[relativeFormatter setLocale: locale];
[absoluteFormatter setLocale: locale];
}
NSCalendar *const calendar = [self calendar];
if([relativeFormatter calendar] != calendar)
{
[relativeFormatter setCalendar: calendar];
[absoluteFormatter setCalendar: calendar];
}
NSString *const maybeRelativeDateString = [relativeFormatter stringFromDate: date];
const BOOL isRelativeDateString = ![maybeRelativeDateString isEqualToString: [absoluteFormatter stringFromDate: date]];
if(isRelativeDateString)
{
return maybeRelativeDateString;
}
else
{
return [self stringFromDate: date];
}
}
@end
It maintains two date formatters using an (arbitrary) standard format. They format identically except that one will provide relative date strings and the other will not.
By formatting a given date with both formatters, it is possible to see if the relative date formatter is giving a special relative date. There is a special relative date when the two formatters give different results.
You can find out more about dispatch_once
here.
The implementation does not handle time components that you may have placed in to your format string. When a relative date string is available, your format sting is ignored and you get the relative date string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With