Brian Dunagan

September 13 2008
Cocoa Tutorial: Yesterday, Today, and Tomorrow with NSDate

One common need is to make dates within the last week more readable, like using “Today” instead of “September 13”. Apple Mail and NetNewsWire both do this. When using Cocoa Bindings, the easiest path to this is through an NSValueTransformer. I initially tried to use NSCalendarDate (following this example), but apparently Apple might deprecate that object in 10.6. So instead I went with NSDateComponents.

Below is code from BDDateTransformer. It converts appropriate dates into “Tomorrow”, “Today”, “Yesterday”, and the past five days of the week. Feel free to use this wherever.

// snippet from BDDateTransformer.m //
- (id)transformedValue:(NSDate *)date
	// Initialize the formatter.
	NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
	[formatter setDateStyle:NSDateFormatterShortStyle];
	[formatter setTimeStyle:NSDateFormatterNoStyle];
	// Initialize the calendar and flags.
	unsigned unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit |  NSDayCalendarUnit | NSWeekdayCalendarUnit;
	NSCalendar *calendar = [NSCalendar currentCalendar];

	// Create reference date for supplied date.
	NSDateComponents *comps = [calendar components:unitFlags fromDate:date];
	[comps setHour:0];
	[comps setMinute:0];
	[comps setSecond:0];
	NSDate *suppliedDate = [calendar dateFromComponents:comps];
	// Iterate through the eight days (tomorrow, today, and the last six).
	int i;
	for (i = -1; i < 7; i++)
		// Initialize reference date.
		comps = [calendar components:unitFlags fromDate:[NSDate date]];
		[comps setHour:0];
		[comps setMinute:0];
		[comps setSecond:0];
		[comps setDay:[comps day] - i];
		NSDate *referenceDate = [calendar dateFromComponents:comps];
		// Get week day (starts at 1).
		int weekday = [[calendar components:unitFlags fromDate:referenceDate] weekday] - 1;
		if ([suppliedDate compare:referenceDate] == NSOrderedSame && i == -1)
			// Tomorrow
			return [NSString stringWithString:@"Tomorrow"];
		else if ([suppliedDate compare:referenceDate] == NSOrderedSame && i == 0)
			// Today's time (a la iPhone Mail)
			[formatter setDateStyle:NSDateFormatterNoStyle];
			[formatter setTimeStyle:NSDateFormatterShortStyle];
			return [formatter stringFromDate:date];
		else if ([suppliedDate compare:referenceDate] == NSOrderedSame && i == 1)
			// Today
			return [NSString stringWithString:@"Yesterday"];
		else if ([suppliedDate compare:referenceDate] == NSOrderedSame)
			// Day of the week
			NSString *day = [[formatter weekdaySymbols] objectAtIndex:weekday];
			return day;
	// It's not in those eight days.
	NSString *defaultDate = [formatter stringFromDate:date];
	return defaultDate;

07/31/2009 UPDATE: I finally added Ford’s code for listing a time for today, like the iPhone’s Mail. I also updated RssBucket on Google Code, where I use this date transformer in context.

Cocoa Tutorial: iTunes Link Arrows Atomic Transactions
LinkedIn GitHub Email