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())
78