Fuzzy date algorithm

There is a property in NSDateFormatter – “doesRelativeDateFormatting”. It appears only in 10.6/iOS4.0 and later but it will format a date into a relative date in the correct locale.

From Apple’s Documentation:

If a date formatter uses relative date
formatting, where possible it replaces
the date component of its output with
a phrase—such as “today” or
“tomorrow”—that indicates a relative
date. The available phrases depend on
the locale for the date formatter;
whereas, for dates in the future,
English may only allow “tomorrow,”
French may allow “the day after the
day after tomorrow,” as illustrated in
the following example.

Code

The following is code that will print out a good number of the relative strings for a given locale.

NSLocale *locale = [NSLocale currentLocale];
//    NSLocale *locale = [[[NSLocale alloc] initWithLocaleIdentifier:@"fr_FR"] autorelease];

NSDateFormatter *relativeDateFormatter = [[NSDateFormatter alloc] init];
[relativeDateFormatter setTimeStyle:NSDateFormatterNoStyle];
[relativeDateFormatter setDateStyle:NSDateFormatterMediumStyle];
[relativeDateFormatter setDoesRelativeDateFormatting:YES];
[relativeDateFormatter setLocale:locale];

NSDateFormatter *normalDateFormatter = [[NSDateFormatter alloc] init];
[normalDateFormatter setTimeStyle:NSDateFormatterNoStyle];
[normalDateFormatter setDateStyle:NSDateFormatterMediumStyle];
[normalDateFormatter setDoesRelativeDateFormatting:NO];
[normalDateFormatter setLocale:locale];

NSString * lastUniqueString = nil;

for ( NSTimeInterval timeInterval = -60*60*24*400; timeInterval < 60*60*24*400; timeInterval += 60.0*60.0*24.0 )
{
    NSDate * date = [NSDate dateWithTimeIntervalSinceNow:timeInterval];

    NSString * relativeFormattedString = [relativeDateFormatter stringForObjectValue:date];
    NSString * formattedString = [normalDateFormatter stringForObjectValue:date];

    if ( [relativeFormattedString isEqualToString:lastUniqueString] || [relativeFormattedString isEqualToString:formattedString] )
        continue;

    NSLog( @"%@", relativeFormattedString );
    lastUniqueString = relativeFormattedString;
}

Notes:

  • A locale is not required
  • There are
    not that many substitutions for
    English. At the time of writing there
    are: “Yesterday, Today, Tomorrow”.
    Apple may include more in the future.
  • It’s fun to change the locale and see
    what is available in other languages
    (French has a few more than English,
    for example)
  • If on iOS, you might want to subscribe to UIApplicationSignificantTimeChangeNotification

Interface Builder

You can set the “doesRelativeDateFormatting” property in Interface Builder:

  • Select your NSDateFormatter and
    choose the “Identity Inspector” tab
    of the Inspector Palette (the last
    one [command-6]).
  • Under the sub-section named “User
    Defined Runtime Attributes”, you can
    add your own value for a key on the selected object (in this case, your NSDateFormatter instance). Add
    “doesRelativeDateFormatting”, choose
    a “Boolean” type, and make sure it’s
    checked.
  • Remember: It may look like it didn’t work at all, but that might because there are only a few substituted values for your locale. Try at least a date for Yesterday, Today, and Tomorrow before you decide if it’s not set up right.

Leave a Comment