EXIF timestamp adjustment

This is a script to modify the timestamps in one or more EXIF tags on JPEG images by a fixed offset. It is useful if you’ve gone overseas, but forgotten to change the clock on your camera, so all the timestamps are incorrectly in the middle of the night (or the day, as the case may be). It requires Benno’s pexif library, version 0.12 or later (it’s also now included in the pexif distribution).

   1 #!/usr/bin/env python
   2 
   3 
   4 Utility to adjust the EXIF timestamps in JPEG files by a constant offset.
   5 
   6 Requires Benno's pexif library: http://code.google.com/p/pexif/
   7 
   8 -- Andrew Baumann andrewb@inf.ethz.ch, 20080716
   9 
  10 
  11 import sys
  12 from pexif import JpegFile, EXIF_OFFSET
  13 from datetime import timedelta, datetime
  14 from optparse import OptionParser
  15 
  16 DATETIME_EMBEDDED_TAGS = [DateTimeOriginal, DateTimeDigitized]
  17 TIME_FORMAT = '%Y:%m:%d %H:%M:%S'
  18 
  19 def parse_args():
  20     p = OptionParser(usage='%prog hours file.jpg...',
  21            description='adjusts timestamps in EXIF metadata by given offset')
  22     options, args = p.parse_args()
  23     if len(args)  2:
  24         p.error('not enough arguments')
  25     try:
  26         hours = int(args[0])
  27     except:
  28         p.error('invalid time offset, must be an integral number of hours')
  29     return hours, args[1:]
  30 
  31 def adjust_time(primary, delta):
  32     def adjust_tag(timetag, delta):
  33         dt = datetime.strptime(timetag, TIME_FORMAT)
  34         dt += delta
  35         return dt.strftime(TIME_FORMAT)
  36 
  37     if primary.DateTime:
  38         primary.DateTime = adjust_tag(primary.DateTime, delta)
  39 
  40     embedded = primary[EXIF_OFFSET]
  41     if embedded:
  42         for tag in DATETIME_EMBEDDED_TAGS:
  43             if embedded[tag]:
  44                 embedded[tag] = adjust_tag(embedded[tag], delta)
  45 
  46 def main():
  47     hours, files = parse_args()
  48     delta = timedelta(hours=hours)
  49 
  50     for fname in files:
  51         try:
  52             jf = JpegFile.fromFile(fname)
  53         except (IOError, JpegFile.InvalidFile):
  54             type, value, traceback = sys.exc_info()
  55             print  sys.stderr, Error reading %s: % fname, value
  56             return 1
  57 
  58         exif = jf.get_exif()
  59         if exif:
  60             primary = exif.get_primary()
  61         if exif is None or primary is None:
  62             print  sys.stderr, %s has no EXIF tag, skipping % fname
  63             continue
  64 
  65         adjust_time(primary, delta)
  66 
  67         try:
  68             jf.writeFile(fname)
  69         except IOError:
  70             type, value, traceback = sys.exc_info()
  71             print  sys.stderr, Error saving %s: % fname, value
  72             return 1
  73 
  74     return 0
  75 
  76 if __name__ == __main__:
  77     sys.exit(main())

exif_timezone.py