aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pyexiv2/exif.py11
-rw-r--r--src/pyexiv2/iptc.py24
-rw-r--r--src/pyexiv2/utils.py156
-rw-r--r--src/pyexiv2/xmp.py28
4 files changed, 171 insertions, 48 deletions
diff --git a/src/pyexiv2/exif.py b/src/pyexiv2/exif.py
index b0c6374..1d56be8 100644
--- a/src/pyexiv2/exif.py
+++ b/src/pyexiv2/exif.py
@@ -2,7 +2,7 @@
# ******************************************************************************
#
-# Copyright (C) 2006-2010 Olivier Tilloy <olivier@tilloy.net>
+# Copyright (C) 2006-2011 Olivier Tilloy <olivier@tilloy.net>
#
# This file is part of the pyexiv2 distribution.
#
@@ -32,7 +32,8 @@ import libexiv2python
from pyexiv2.utils import is_fraction, make_fraction, fraction_to_string, \
NotifyingList, ListenerInterface, \
- undefined_to_string, string_to_undefined
+ undefined_to_string, string_to_undefined, \
+ DateTimeFormatter
import time
import datetime
@@ -347,13 +348,13 @@ class ExifTag(ListenerInterface):
"""
if self.type == 'Ascii':
if isinstance(value, datetime.datetime):
- return value.strftime(self._datetime_formats[0])
+ return DateTimeFormatter.exif(value)
elif isinstance(value, datetime.date):
if self.key == 'Exif.GPSInfo.GPSDateStamp':
# Special case
- return value.strftime(self._date_formats[0])
+ return DateTimeFormatter.exif(value)
else:
- return value.strftime('%s 00:00:00' % self._date_formats[0])
+ return '%s 00:00:00' % DateTimeFormatter.exif(value)
elif isinstance(value, unicode):
try:
return value.encode('utf-8')
diff --git a/src/pyexiv2/iptc.py b/src/pyexiv2/iptc.py
index f2a360d..c6ed36f 100644
--- a/src/pyexiv2/iptc.py
+++ b/src/pyexiv2/iptc.py
@@ -2,7 +2,7 @@
# ******************************************************************************
#
-# Copyright (C) 2006-2010 Olivier Tilloy <olivier@tilloy.net>
+# Copyright (C) 2006-2011 Olivier Tilloy <olivier@tilloy.net>
#
# This file is part of the pyexiv2 distribution.
#
@@ -30,7 +30,8 @@ IPTC specific code.
import libexiv2python
-from pyexiv2.utils import ListenerInterface, NotifyingList, FixedOffset
+from pyexiv2.utils import ListenerInterface, NotifyingList, \
+ FixedOffset, DateTimeFormatter
import time
import datetime
@@ -335,28 +336,13 @@ class IptcTag(ListenerInterface):
elif self.type == 'Date':
if isinstance(value, (datetime.date, datetime.datetime)):
- # ISO 8601 date format.
- # According to the IPTC specification, the format for a string
- # field representing a date is '%Y%m%d'. However, the string
- # expected by exiv2's DateValue::read(string) should be
- # formatted using pattern '%Y-%m-%d'.
- return value.strftime('%Y-%m-%d')
+ return DateTimeFormatter.iptc_date(value)
else:
raise IptcValueError(value, self.type)
elif self.type == 'Time':
if isinstance(value, (datetime.time, datetime.datetime)):
- # According to the IPTC specification, the format for a string
- # field representing a time is '%H%M%S±%H%M'. However, the
- # string expected by exiv2's TimeValue::read(string) should be
- # formatted using pattern '%H:%M:%S±%H:%M'.
- r = value.strftime('%H:%M:%S')
- if value.tzinfo is not None:
- s = value.strftime('%z') # of the form ±%H%M
- r += s[:3] + ':' + s[3:]
- else:
- r += '+00:00'
- return r
+ return DateTimeFormatter.iptc_time(value)
else:
raise IptcValueError(value, self.type)
diff --git a/src/pyexiv2/utils.py b/src/pyexiv2/utils.py
index de56c21..9473aff 100644
--- a/src/pyexiv2/utils.py
+++ b/src/pyexiv2/utils.py
@@ -2,7 +2,7 @@
# ******************************************************************************
#
-# Copyright (C) 2006-2010 Olivier Tilloy <olivier@tilloy.net>
+# Copyright (C) 2006-2011 Olivier Tilloy <olivier@tilloy.net>
#
# This file is part of the pyexiv2 distribution.
#
@@ -573,3 +573,157 @@ class GPSCoordinate(object):
return '%d,%d,%d%s' % (self._degrees, self._minutes, self._seconds,
self._direction)
+
+class DateTimeFormatter(object):
+
+ """
+ Convenience object that exposes static methods to convert a date, time or
+ datetime object to a string representation suitable for various metadata
+ standards.
+
+ This is needed because python’s
+ `strftime() <http://docs.python.org/library/datetime.html#strftime-strptime-behavior>`_
+ doesn’t work for years before 1900.
+
+ This class mostly exists for internal usage only. Clients should never need
+ to use it.
+ """
+
+ @staticmethod
+ def timedelta_to_offset(t):
+ """
+ Convert a time delta to a string representation in the form ``±%H:%M``.
+
+ :param t: a time delta
+ :type t: :class:`datetime.timedelta`
+
+ :return: a string representation of the time delta in the form
+ ``±%H:%M``
+ :rtype: string
+ """
+ seconds = t.total_seconds()
+ hours = int(seconds / 3600)
+ minutes = abs(int((seconds - hours * 3600) / 60))
+ return '%+03d:%02d' % (hours, minutes)
+
+ @staticmethod
+ def exif(d):
+ """
+ Convert a date/time object to a string representation conforming to
+ libexiv2’s internal representation for the EXIF standard.
+
+ :param d: a datetime or date object
+ :type d: :class:`datetime.datetime` or :class:`datetime.date`
+
+ :return: a string representation conforming to the EXIF standard
+ :rtype: string
+
+ :raise TypeError: if the parameter is not a datetime or a date object
+ """
+ if isinstance(d, datetime.datetime):
+ return '%04d:%02d:%02d %02d:%02d:%02d' % \
+ (d.year, d.month, d.day, d.hour, d.minute, d.second)
+ elif isinstance(d, datetime.date):
+ return '%04d:%02d:%02d' % (d.year, d.month, d.day)
+ else:
+ raise TypeError('expecting an object of type '
+ 'datetime.datetime or datetime.date')
+
+ @staticmethod
+ def iptc_date(d):
+ """
+ Convert a date object to a string representation conforming to
+ libexiv2’s internal representation for the IPTC standard.
+
+ :param d: a datetime or date object
+ :type d: :class:`datetime.datetime` or :class:`datetime.date`
+
+ :return: a string representation conforming to the IPTC standard
+ :rtype: string
+
+ :raise TypeError: if the parameter is not a datetime or a date object
+ """
+ if isinstance(d, (datetime.date, datetime.datetime)):
+ # ISO 8601 date format.
+ # According to the IPTC specification, the format for a string
+ # field representing a date is '%Y%m%d'. However, the string
+ # expected by exiv2's DateValue::read(string) should be
+ # formatted using pattern '%Y-%m-%d'.
+ return '%04d-%02d-%02d' % (d.year, d.month, d.day)
+ else:
+ raise TypeError('expecting an object of type '
+ 'datetime.datetime or datetime.date')
+
+ @staticmethod
+ def iptc_time(d):
+ """
+ Convert a time object to a string representation conforming to
+ libexiv2’s internal representation for the IPTC standard.
+
+ :param d: a datetime or time object
+ :type d: :class:`datetime.datetime` or :class:`datetime.time`
+
+ :return: a string representation conforming to the IPTC standard
+ :rtype: string
+
+ :raise TypeError: if the parameter is not a datetime or a time object
+ """
+ if isinstance(d, (datetime.time, datetime.datetime)):
+ # According to the IPTC specification, the format for a string
+ # field representing a time is '%H%M%S±%H%M'. However, the
+ # string expected by exiv2's TimeValue::read(string) should be
+ # formatted using pattern '%H:%M:%S±%H:%M'.
+ r = '%02d:%02d:%02d' % (d.hour, d.minute, d.second)
+ if d.tzinfo is not None:
+ t = d.utcoffset()
+ if t is not None:
+ r += DateTimeFormatter.timedelta_to_offset(t)
+ else:
+ r += '+00:00'
+ return r
+ else:
+ raise TypeError('expecting an object of type '
+ 'datetime.datetime or datetime.time')
+
+ @staticmethod
+ def xmp(d):
+ """
+ Convert a date/time object to a string representation conforming to
+ libexiv2’s internal representation for the XMP standard.
+
+ :param d: a datetime or date object
+ :type d: :class:`datetime.datetime` or :class:`datetime.date`
+
+ :return: a string representation conforming to the XMP standard
+ :rtype: string
+
+ :raise TypeError: if the parameter is not a datetime or a date object
+ """
+ if isinstance(d, datetime.datetime):
+ t = d.utcoffset()
+ if d.tzinfo is None or t is None or t == datetime.timedelta(0):
+ tz = 'Z'
+ else:
+ tz = DateTimeFormatter.timedelta_to_offset(t)
+ if d.hour == 0 and d.minute == 0 and \
+ d.second == 0 and d.microsecond == 0 and \
+ (d.tzinfo is None or d.utcoffset() == datetime.timedelta(0)):
+ return '%04d-%02d-%02d' % (d.year, d.month, d.day)
+ elif d.second == 0 and d.microsecond == 0:
+ return '%04d-%02d-%02dT%02d:%02d%s' % \
+ (d.year, d.month, d.day, d.hour, d.minute, tz)
+ elif d.microsecond == 0:
+ return '%04d-%02d-%02dT%02d:%02d:%02d%s' % \
+ (d.year, d.month, d.day, d.hour, d.minute, d.second, tz)
+ else:
+ r = '%04d-%02d-%02dT%02d:%02d:%02d.' % \
+ (d.year, d.month, d.day, d.hour, d.minute, d.second)
+ r += str(int(d.microsecond) / 1E6)[2:]
+ r += tz
+ return r
+ elif isinstance(d, datetime.date):
+ return '%04d-%02d-%02d' % (d.year, d.month, d.day)
+ else:
+ raise TypeError('expecting an object of type '
+ 'datetime.datetime or datetime.date')
+
diff --git a/src/pyexiv2/xmp.py b/src/pyexiv2/xmp.py
index 7240527..bed1b66 100644
--- a/src/pyexiv2/xmp.py
+++ b/src/pyexiv2/xmp.py
@@ -2,7 +2,7 @@
# ******************************************************************************
#
-# Copyright (C) 2006-2010 Olivier Tilloy <olivier@tilloy.net>
+# Copyright (C) 2006-2011 Olivier Tilloy <olivier@tilloy.net>
#
# This file is part of the pyexiv2 distribution.
#
@@ -30,7 +30,8 @@ XMP specific code.
import libexiv2python
-from pyexiv2.utils import FixedOffset, is_fraction, make_fraction, GPSCoordinate
+from pyexiv2.utils import FixedOffset, is_fraction, make_fraction, \
+ GPSCoordinate, DateTimeFormatter
import datetime
import re
@@ -387,27 +388,8 @@ class XmpTag(object):
raise XmpValueError(value, type)
elif type == 'Date':
- if isinstance(value, datetime.datetime):
- if value.tzinfo is None or value.utcoffset() == datetime.timedelta(0):
- tz = 'Z'
- else:
- tz = value.strftime('%z') # of the form ±%H%M
- tz = tz[:3] + ':' + tz[3:]
- if value.hour == 0 and value.minute == 0 and \
- value.second == 0 and value.microsecond == 0 and \
- (value.tzinfo is None or value.utcoffset() == datetime.timedelta(0)):
- return value.strftime('%Y-%m-%d')
- elif value.second == 0 and value.microsecond == 0:
- return value.strftime('%Y-%m-%dT%H:%M') + tz
- elif value.microsecond == 0:
- return value.strftime('%Y-%m-%dT%H:%M:%S') + tz
- else:
- r = value.strftime('%Y-%m-%dT%H:%M:%S.')
- r += str(int(value.microsecond) / 1E6)[2:]
- r += tz
- return r
- elif isinstance(value, datetime.date):
- return value.isoformat()
+ if isinstance(value, (datetime.date, datetime.datetime)):
+ return DateTimeFormatter.xmp(value)
else:
raise XmpValueError(value, type)