Index: django/db/models/expressions.py
===================================================================
--- django/db/models/expressions.py	(revision 12380)
+++ django/db/models/expressions.py	(working copy)
@@ -1,4 +1,4 @@
-from datetime import datetime
+import datetime
 
 from django.utils import tree
 from django.utils.copycompat import deepcopy
@@ -26,6 +26,9 @@
         super(ExpressionNode, self).__init__(children, connector, negated)
 
     def _combine(self, other, connector, reversed, node=None):
+        if isinstance(other, datetime.timedelta):
+            return DateModifierNode([self, other], connector)
+
         if reversed:
             obj = ExpressionNode([other], connector)
             obj.add(node or self, connector)
@@ -111,3 +114,43 @@
 
     def evaluate(self, evaluator, qn, connection):
         return evaluator.evaluate_leaf(self, qn, connection)
+
+class DateModifierNode(ExpressionNode):
+    """
+    Node that implements the following syntax:
+    filter(end_date__gt=F('start_date') + datetime.timedelta(days=3, seconds=200))
+
+    which translates into:
+    SQLITE:
+        WHERE end_date > DATE(start_date, "+3 days", "+200 seconds")
+
+    POSTGRES:
+        WHERE end_date > (start_date + INTERVAL '3 days 200 seconds')
+
+    MYSQL:
+        WHERE end_date > (start_date + INTERVAL '3 0:0:200:0' DAY_MICROSECOND)
+
+    ORACLE:
+        WHERE end_date > (start_date + INTERVAL '3 00:03:20.000000' DAY(1) TO SECOND(6))
+
+    Note that microsecond comparisons are not well supported with sqlite or
+    mysql.
+    """
+    def __init__(self, children, connector, negated=False):
+        if len(children) != 2:
+            raise TypeError('Must specify a node and a timedelta.')
+        if not isinstance(children[1], datetime.timedelta):
+            raise TypeError('Second child must be a timedelta.')
+        if connector is None:
+            raise TypeError('Must specify a connector.')
+        super(DateModifierNode, self).__init__(children, connector, negated)
+
+    def evaluate(self, evaluator, qn, connection):
+        timedelta = self.children.pop()
+        sql, params = evaluator.evaluate_node(self, qn, connection)
+
+        if timedelta.days == 0 and timedelta.seconds == 0 and \
+                timedelta.microseconds == 0:
+            return sql, params
+
+        return connection.ops.date_interval_sql(sql, self.connector, timedelta), params
Index: django/db/backends/postgresql/operations.py
===================================================================
--- django/db/backends/postgresql/operations.py	(revision 12380)
+++ django/db/backends/postgresql/operations.py	(working copy)
@@ -27,6 +27,23 @@
         else:
             return "EXTRACT('%s' FROM %s)" % (lookup_type, field_name)
 
+    def date_interval_sql(self, sql, connector, timedelta):
+        """
+        implements the interval functionality for expressions
+        format for Postgres:
+            (datefield + interval '3 days 200 seconds 5 microseconds')
+        """
+        modifiers = []
+        if timedelta.days:
+            modifiers.append(u'%s days' % timedelta.days)
+        if timedelta.seconds:
+            modifiers.append(u'%s seconds' % timedelta.seconds)
+        if timedelta.microseconds:
+            modifiers.append(u'%s microseconds' % timedelta.microseconds)
+        mods = u' '.join(modifiers)
+        conn = u' %s ' % connector
+        return u'(%s)' % conn.join([sql, u'interval \'%s\'' % mods])
+
     def date_trunc_sql(self, lookup_type, field_name):
         # http://www.postgresql.org/docs/8.0/static/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
         return "DATE_TRUNC('%s', %s)" % (lookup_type, field_name)
Index: django/db/backends/sqlite3/base.py
===================================================================
--- django/db/backends/sqlite3/base.py	(revision 12380)
+++ django/db/backends/sqlite3/base.py	(working copy)
@@ -66,6 +66,20 @@
         # function django_extract that's registered in connect().
         return 'django_extract("%s", %s)' % (lookup_type.lower(), field_name)
 
+    def date_interval_sql(self, sql, connector, timedelta):
+        """
+        Implements the interval functionality for expressions
+        format for sqlite: DATETIME(datefield, "+3 days", "+200 seconds")
+        """
+        modifiers = []
+        if timedelta.days:
+            modifiers.append(u'%s days' % timedelta.days)
+        if timedelta.seconds:
+            fs = timedelta.seconds + (timedelta.microseconds / 1000000.)
+            modifiers.append(u'%s seconds' % fs)
+        mods = u','.join([u'"%s%s"' % (connector, modifier) for modifier in modifiers])
+        return u'DATETIME(%s, %s)' % (sql, mods)
+
     def date_trunc_sql(self, lookup_type, field_name):
         # sqlite doesn't support DATE_TRUNC, so we fake it with a user-defined
         # function django_date_trunc that's registered in connect().
Index: django/db/backends/mysql/base.py
===================================================================
--- django/db/backends/mysql/base.py	(revision 12380)
+++ django/db/backends/mysql/base.py	(working copy)
@@ -147,6 +147,10 @@
             sql = "CAST(DATE_FORMAT(%s, '%s') AS DATETIME)" % (field_name, format_str)
         return sql
 
+    def date_interval_sql(self, sql, connector, timedelta):
+        return "(%s %s INTERVAL '%d 0:0:%d:%d' DAY_MICROSECOND)" % (sql, connector, 
+                timedelta.days, timedelta.seconds, timedelta.microseconds)
+
     def drop_foreignkey_sql(self):
         return "DROP FOREIGN KEY"
 
Index: django/db/backends/oracle/base.py
===================================================================
--- django/db/backends/oracle/base.py	(revision 12380)
+++ django/db/backends/oracle/base.py	(working copy)
@@ -95,6 +95,20 @@
         else:
             return "EXTRACT(%s FROM %s)" % (lookup_type, field_name)
 
+    def date_interval_sql(self, sql, connector, timedelta):
+        """
+        Implements the interval functionality for expressions
+        format for Oracle:
+        (datefield + INTERVAL '3 00:03:20.000000' DAY(1) TO SECOND(6))
+        """
+        minutes, seconds = divmod(timedelta.seconds, 60)
+        hours, minutes = divmod(minutes, 60)
+        days = str(timedelta.days)
+        day_precision = len(days)
+        fmt = "(%s %s INTERVAL '%s %02d:%02d:%02d.%06d' DAY(%d) TO SECOND(6))"
+        return fmt % (sql, connector, days, hours, minutes, seconds, 
+                timedelta.microseconds, day_precision)
+
     def date_trunc_sql(self, lookup_type, field_name):
         # Oracle uses TRUNC() for both dates and numbers.
         # http://download-east.oracle.com/docs/cd/B10501_01/server.920/a96540/functions155a.htm#SQLRF06151
Index: django/db/backends/__init__.py
===================================================================
--- django/db/backends/__init__.py	(revision 12380)
+++ django/db/backends/__init__.py	(working copy)
@@ -123,6 +123,12 @@
         """
         raise NotImplementedError()
 
+    def date_interval_sql(self, sql, connector, timedelta):
+        """
+        Implements the date interval functionality for expressions
+        """
+        raise NotImplementedError()
+
     def date_trunc_sql(self, lookup_type, field_name):
         """
         Given a lookup_type of 'year', 'month' or 'day', returns the SQL that
Index: tests/modeltests/expressions/models.py
===================================================================
--- tests/modeltests/expressions/models.py	(revision 12380)
+++ tests/modeltests/expressions/models.py	(working copy)
@@ -7,6 +7,8 @@
 class Employee(models.Model):
     firstname = models.CharField(max_length=50)
     lastname = models.CharField(max_length=50)
+    contract_date = models.DateTimeField(null=True)
+    start_date = models.DateTimeField(null=True)
 
     def __unicode__(self):
         return u'%s %s' % (self.firstname, self.lastname)
@@ -29,13 +31,20 @@
 
 __test__ = {'API_TESTS': """
 >>> from django.db.models import F
+>>> import datetime
 
 >>> Company(name='Example Inc.', num_employees=2300, num_chairs=5,
-...     ceo=Employee.objects.create(firstname='Joe', lastname='Smith')).save()
+...     ceo=Employee.objects.create(firstname='Joe', lastname='Smith', 
+...             contract_date=datetime.datetime(2008,1,1),
+...             start_date=datetime.datetime(2008,3,1))).save()
 >>> Company(name='Foobar Ltd.', num_employees=3, num_chairs=3,
-...     ceo=Employee.objects.create(firstname='Frank', lastname='Meyer')).save()
+...     ceo=Employee.objects.create(firstname='Frank', lastname='Meyer',
+...             contract_date=datetime.datetime(2008,1,1),
+...             start_date=datetime.datetime(2008,5,1))).save()
 >>> Company(name='Test GmbH', num_employees=32, num_chairs=1,
-...     ceo=Employee.objects.create(firstname='Max', lastname='Mustermann')).save()
+...     ceo=Employee.objects.create(firstname='Max', lastname='Mustermann',
+...             contract_date=datetime.datetime(2008,1,1),
+...             start_date=datetime.datetime(2008,7,1))).save()
 
 >>> company_query = Company.objects.values('name','num_employees','num_chairs').order_by('name','num_employees','num_chairs')
 
@@ -56,6 +65,28 @@
 >>> company_query
 [{'num_chairs': 2302, 'name': u'Example Inc.', 'num_employees': 2300}, {'num_chairs': 5, 'name': u'Foobar Ltd.', 'num_employees': 3}, {'num_chairs': 34, 'name': u'Test GmbH', 'num_employees': 32}]
 
+# F expressions for dates may be combined with timedelta
+# in filter:
+>>> Employee.objects.filter(start_date__gt=F('contract_date')+datetime.timedelta(days=80))
+[<Employee: Frank Meyer>, <Employee: Max Mustermann>]
+
+>>> Employee.objects.filter(contract_date__gt=F('start_date')-datetime.timedelta(days=80))
+[<Employee: Joe Smith>]
+
+>>> Employee.objects.filter(start_date__gt=F('contract_date')+datetime.timedelta(days=80)+datetime.timedelta(seconds=500))
+[<Employee: Frank Meyer>, <Employee: Max Mustermann>]
+
+# in update:
+# move contract dates to 14 days before start date
+>>> Employee.objects.update(contract_date=F('start_date') - datetime.timedelta(days=14))
+3
+>>> [e.contract_date for e in Employee.objects.all()]
+[datetime.datetime(2008, 2, 16, 0, 0), datetime.datetime(2008, 4, 17, 0, 0), datetime.datetime(2008, 6, 17, 0, 0)]
+
+# Zero timedeltas:
+>>> Employee.objects.filter(start_date__gt=F('contract_date') + datetime.timedelta(days=0))
+[<Employee: Joe Smith>, <Employee: Frank Meyer>, <Employee: Max Mustermann>]
+
 # Law of order of operations is followed
 >>> _ =company_query.update(num_chairs=F('num_employees') + 2 * F('num_employees'))
 >>> company_query
Index: docs/topics/db/queries.txt
===================================================================
--- docs/topics/db/queries.txt	(revision 12380)
+++ docs/topics/db/queries.txt	(working copy)
@@ -38,6 +38,7 @@
         headline = models.CharField(max_length=255)
         body_text = models.TextField()
         pub_date = models.DateTimeField()
+        mod_date = models.DateTimeField()
         authors = models.ManyToManyField(Author)
         n_comments = models.IntegerField()
         n_pingbacks = models.IntegerField()
@@ -534,6 +535,13 @@
 
     >>> Entry.objects.filter(author__name=F('blog__name'))
 
+For date fields, you can add or subtract a ``datetime.timedelta`` object.  The
+following would return all entries that were modified more than 3 days after
+they were published:
+   
+    >>> from datetime import timedelta
+    >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))
+
 The pk lookup shortcut
 ----------------------
 
