Parsing Email Date Formats

I’ve been busily working towards the release of LicenseKeeper 1.2 over the past week. This morning I attacked a minor issue where some email received dates weren’t being parsed correctly.

After some investigation, it turns out that is was just a matter of handling an additional string to date conversion format. The problem and solution were fairly trivial, but my first and very lazy approach oozed ugly:

// Apple Mail style date... 
//      (ex: Sat, 24 Mar 2007 21:51:06 +0100)
_dateReceived = [NSCalendarDate dateWithString:dateString 
                   calendarFormat:@"%a, %d %b %Y %I:%M:%S %z"];
if (!_dateReceived) {
   // Entourage style date... 
   //   (ex: Tuesday, March 13, 2007 3:46 PM)
   _dateReceived = [NSCalendarDate dateWithString:dateString 
                   calendarFormat:@"%A, %B %e, %Y %I:%M %p"];
}
if (!_dateReceived) {
   // short style date with no time zone... 
   //   (ex: Fri, 1 Jun 2007 14:24:56)
   _dateReceived = [NSCalendarDate dateWithString:dateString 
                   calendarFormat:@"%a, %d %b %Y %I:%M:%S"];
}

Originally, I just had two date formats to parse so one “if” statement didn’t look to bad. But, it also had some nasty hard coded string constants.

Once I added the third date format, the code really started to look ugly and started to reek of code rot: too many hard-coded strings and cut-and-paste code blocks.

So, I refactored the code to move the date format strings to the application’s Info.plist file:

OLEmailDateFormats
      
            %a, %d %b %Y %I:%M:%S %z
            %A, %B %e, %Y %I:%M %p
            %a, %d %b %Y %I:%M:%S
       

Now with the date formats moved to a more manageable and reusable place, the date parsing code block cleans up quite nicely.

NSArray *dateFormats = [[[NSBundle mainBundle] infoDictionary]
                             valueForKey:@"OLEmailDateFormats"];
int i;
for (i = 0; i < [dateFormats count]; i++) {
   _dateReceived = [NSCalendarDate dateWithString:dateString 
                    calendarFormat:[dateFormats objectAtIndex:i]];
   if (_dateReceived) {
      break;
   }
}

The big advantage here is that if I need to add more date formats to the parser the code doesn’t become more complicated and run the risk of adding new bugs down the line. It also eliminates the need to recompile the source file if all I’m doing is adding a new format.

Plus the code just looks nicer and is easier to understand.

2 thoughts on “Parsing Email Date Formats

  1. Cool move. I ran into a similar problem with MarsEdit, which of course has to make sense of the date strings that come along with downloaded blog posts from various systems. At some point one of the systems changed its format subtly and I had to add another case as you have done.

    It would be a great idea for me to follow your lead and put them in an editable resource. A really nice side-effect of doing this is that you can prescribe an “emergency fix” for power users to apply without requiring an update of the code.

  2. I’ve been making more and more use of this pattern lately. Supporting multiple external systems that have similar behavior but slightly different formats lends itself nicely to this approach.

Comments are closed.