Fixing received dates on IMAP messages

I recently tidied up much of my mail. It is all stored in Maildir++ form and I used to use Cyrus IMAP to access it. Many months ago I moved to Dovecot, but only recently renamed the directories from '.INBOX.Example" to ".Example".

To test that Dovecot, my mail reader, prostfix, sieve, deliver, etc would all work correctly I backed up one directoy, renamed it and used it as that test. It all worked correctly, so I copied the backup over the original -- then I noticed that Apple Mail suddenly was convinced that all of the mail in that folder had arrived at the time that I copied the messages. A little poking around showed that Apple Mail uses the file modification time as the received time.


I threw together a small Python script to open each file, parse out the time and date and then update the modification time to be correct.


This script requires mx.DateTime from so that it can correctly parse the mail date.


#!/usr/bin/python2.4 # Copyright 2006-2007 Warren Kumari. All Rights Reserved. """Fixed the date on messages in an Maildir++ style mailbox.. If you copy a Maildir++ style mailbox (and aren't careful!) the dates on the messages all show up as the date the the files were copied (at least in some  mail applications (eg: Apple Mail). This program opens each message, reads the  date from the message and then sets the file time to be correct. """ # This program requires mx.DateTime.ARPA to correctly parse the date and  # time. __author__ = '(Warren Kumari)' import glob # My habit is to call logging.debug, etc. This allows overlap. import logging as Logging import mx.DateTime import optparse import os import re import sys def SetupLogging(name, level = Logging.INFO): """Creates a logger Args: The name of the logger to create (usually argv[0]) Returns: A logger object Synopsis: logging = SetupLogging(argv[0]) """ logger = Logging.getLogger(name) logger.setLevel(level) ch = Logging.StreamHandler() ch.setLevel(level) formatter = Logging.Formatter("%(name)s-%(levelname)s: %(message)s") ch.setFormatter(formatter) logger.addHandler(ch) return logger def GetDate (message): """This finds and returns the date in the supplied message. Args: message: A string (or array of strings) containing a mail message Returns: A string containing the date (or None) if it cannot be found. """ for line in message: m = re.match (r'^Date: (.*)', line) if m: date = try: converted_date = mx.DateTime.ARPA.ParseDateTime(date) return converted_date except ValueError, e: logging.error ("Unable to parse date %s: %s" % (date,e)) return None return None def main(): """Opens each file, parses the date and then sets the date / time""" parser = optparse.OptionParser() parser.add_option("-v", "--verbose", dest="verbose", action="store_true", help="verbose logging") parser.add_option("-d", "--directory", dest="directory", default='.', help="directory to work on") (options, args) = parser.parse_args() if options.verbose:                                  logging = SetupLogging(sys.argv[0], Logging.DEBUG) else: logging = SetupLogging(sys.argv[0], Logging.INFO) logging.debug ("Going to work in %s" % filelist = glob.glob(os.path.join (, "*")) for filename in filelist: logging.debug ("Opening %s" % filename) infile = open(filename, "r") message = infile.readlines() date = GetDate(message) if date is not None: ("Setting date of %s to %s" % (filename, date)) os.utime (filename, (date, date)) else: logging.warn("Unable to parse date from %s" % filename) if __name__ == '__main__': main()