Index: django/db/models/fields/duration.py
===================================================================
--- django/db/models/fields/duration.py	(revision 0)
+++ django/db/models/fields/duration.py	(revision 0)
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+from datetime import timedelta
+from django.forms.fields import DurationField as FDurationField
+from django.db.models.fields import Field
+from django.core.exceptions import ValidationError
+from django.db import connection
+from django.utils import timestring
+
+class DurationField(Field):
+    def __init__(self, *args, **kwargs):
+        super(DurationField, self).__init__(*args, **kwargs)
+        self.max_digits, self.decimal_places = 20, 6
+
+    def get_internal_type(self):
+        return "BigIntegerField"
+
+    def get_db_prep_save(self, value):
+        if value is None:
+            return None # db NULL
+        if isinstance(value, int) or isinstance(value, long):
+            value = timedelta(microseconds=value)
+        return value.days * 24 * 3600 * 1000000 + value.seconds * 1000000 + value.microseconds
+
+    def to_python(self, value):
+        return value
+
+    def formfield(self, form_class=FDurationField, **kwargs):
+        return super(DurationField, self).formfield(form_class, **kwargs)
Index: django/forms/fields.py
===================================================================
--- django/forms/fields.py	(revision 12267)
+++ django/forms/fields.py	(working copy)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Field classes.
 """
@@ -17,17 +18,19 @@
 from django.core.exceptions import ValidationError
 from django.core import validators
 import django.utils.copycompat as copy
+from django.utils.datastructures import SortedDict
 from django.utils.translation import ugettext_lazy as _
 from django.utils.encoding import smart_unicode, smart_str
 from django.utils.formats import get_format
 from django.utils.functional import lazy
+from django.utils.timestring import to_timedelta
 
 # Provide this import for backwards compatibility.
 from django.core.validators import EMPTY_VALUES
 
 from util import ErrorList
 from widgets import TextInput, PasswordInput, HiddenInput, MultipleHiddenInput, \
-        FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, \
+       DurationInput,  FileInput, CheckboxInput, Select, NullBooleanSelect, SelectMultiple, \
         DateInput, DateTimeInput, TimeInput, SplitDateTimeWidget, SplitHiddenDateTimeWidget
 
 __all__ = (
@@ -841,7 +844,48 @@
             return datetime.datetime.combine(*data_list)
         return None
 
+ 
+class DurationField(Field):
+    widget = DurationInput
+    default_error_messages = {
+        'invalid': u'Enter a valid duration.',
+        'max_value': _(u'Ensure this value is less than or equal to %(limit_value)s.'),
+        'min_value': _(u'Ensure this value is greater than or equal to %(limit_value)s.'),
+    }
 
+    def __init__(self, min_value=None, max_value=None, *args, **kwargs):
+        super(DurationField, self).__init__(*args, **kwargs)
+
+        if max_value is not None:
+            self.validators.append(validators.MaxValueValidator(max_value))
+        if min_value is not None:
+            self.validators.append(validators.MinValueValidator(min_value))
+
+    def clean(self, value):
+        """
+        Validates max_value and min_value.
+        Returns a datetime.timedelta object.
+        """
+        try:
+            return to_timedelta(value)
+        except ValueError, e:
+            raise ValidationError(e)
+
+        if self.max_value is not None and value > self.max_value:
+            raise ValidationError(self.error_messages['max_value'] % {'max': self.max_value})
+
+        if self.min_value is not None and value < self.min_value:
+            raise ValidationError(self.error_messages['min_value'] % {'min': self.min_value})
+
+        return value
+
+    def to_python(self, value):
+        try:
+            return to_timedelta(value)
+        except ValueError, e:
+            raise ValidationError(e)
+
+
 class IPAddressField(CharField):
     default_error_messages = {
         'invalid': _(u'Enter a valid IPv4 address.'),
Index: django/forms/widgets.py
===================================================================
--- django/forms/widgets.py	(revision 12267)
+++ django/forms/widgets.py	(working copy)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 HTML Widget classes
 """
@@ -10,8 +11,9 @@
 from django.utils.translation import ugettext
 from django.utils.encoding import StrAndUnicode, force_unicode
 from django.utils.safestring import mark_safe
+from django.utils.timestring import from_timedelta
 from django.utils import datetime_safe, formats
-from datetime import time
+from datetime import time, timedelta
 from util import flatatt
 from urlparse import urljoin
 
@@ -593,7 +595,7 @@
             option_value = force_unicode(option_value)
             rendered_cb = cb.render(name, option_value)
             option_label = conditional_escape(force_unicode(option_label))
-            output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label))
+            output.append(u'<li>%(cb)s<label%(for)s>%(label)s</label></li>' % {"for": label_for, "label": option_label, "cb": rendered_cb})
         output.append(u'</ul>')
         return mark_safe(u'\n'.join(output))
 
@@ -699,6 +701,17 @@
             media = media + w.media
         return media
     media = property(_get_media)
+ 
+class DurationInput(TextInput):
+    def render(self, name, value, attrs=None):
+        if value is None: value = ''
+        final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
+        if value != '':
+            # Only add the 'value' attribute if a value is non-empty.
+            if isinstance(value, int) or isinstance(value, long): # Database backends serving different types
+                value = from_timedelta(timedelta(microseconds=value))
+            final_attrs['value'] = force_unicode(formats.localize_input(value))
+        return mark_safe(u'<input%s />' % flatatt(final_attrs))
 
 class SplitDateTimeWidget(MultiWidget):
     """
Index: django/shortcuts/__init__.py
===================================================================
--- django/shortcuts/__init__.py	(revision 12267)
+++ django/shortcuts/__init__.py	(working copy)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 This module collects helper functions and classes that "span" multiple levels
 of MVC. In other words, these functions/classes introduce controlled coupling
@@ -23,16 +24,16 @@
     """
     Returns an HttpResponseRedirect to the apropriate URL for the arguments
     passed.
-    
+
     The arguments could be:
-    
+
         * A model: the model's `get_absolute_url()` function will be called.
-    
+
         * A view name, possibly with arguments: `urlresolvers.reverse()` will
           be used to reverse-resolve the name.
-         
+
         * A URL, which will be used as-is for the redirect location.
-        
+
     By default issues a temporary redirect; pass permanent=True to issue a
     permanent redirect
     """
@@ -40,11 +41,11 @@
         redirect_class = HttpResponsePermanentRedirect
     else:
         redirect_class = HttpResponseRedirect
-    
+
     # If it's a model, use get_absolute_url()
     if hasattr(to, 'get_absolute_url'):
         return redirect_class(to.get_absolute_url())
-    
+
     # Next try a reverse URL resolution.
     try:
         return redirect_class(urlresolvers.reverse(to, args=args, kwargs=kwargs))
@@ -55,7 +56,7 @@
         # If this doesn't "feel" like a URL, re-raise.
         if '/' not in to and '.' not in to:
             raise
-        
+
     # Finally, fall back and assume it's a URL
     return redirect_class(to)
 
Index: django/utils/timestring.py
===================================================================
--- django/utils/timestring.py	(revision 0)
+++ django/utils/timestring.py	(revision 0)
@@ -0,0 +1,54 @@
+# -*- coding: utf-8 -*-
+"""
+Utility functions to convert back and forth between a timestring and timedelta.
+  1y 7m 6w 3d 18h 30min 23s 10ms 150mis
+    => 1 year 7 months 6 weeks 3 days 18 hours 30 minutes 23 seconds 10 milliseconds 150 microseconds
+    => datetime.timedelta(624, 6155, 805126)
+"""
+
+from datetime import timedelta
+from django.utils.datastructures import SortedDict
+
+values_in_microseconds = SortedDict((
+    # Uncomment the following two lines for year and month support
+    # ('y', 31556925993600), # 52.177457 * (7*24*60*60*1000*1000)
+    # ('m', 2629743832800), # 4.34812141 * (7*24*60*60*1000*1000)
+    ('w', 604800000000), # 7*24*60*60*1000*1000
+    ('d', 86400000000), # 24*60*60*1000*1000
+    ('h', 3600000000), # 60*60*1000*1000
+    ('min', 60000000), # 60*1000*1000
+    ('s',  1000000), # 1000*1000
+    ('ms', 1000),
+    ('us', 1),
+))
+
+def to_timedelta(value):
+    chunks = []
+    for b in value.lower().split():
+        for index, char in enumerate(b):
+            if not char.isdigit():
+                chunks.append((b[:index], b[index:])) # digits, letters
+                break
+
+    microseconds = 0
+    for digits, chars in chunks:
+        if not digits or not chars in values_in_microseconds:
+            raise ValueError('Incorrect timestring pair')
+        microseconds += int(digits) * values_in_microseconds[chars]
+
+    return timedelta(microseconds=microseconds)
+
+def from_timedelta(value):
+    if not value:
+        return u'0s'
+
+    if not isinstance(value, timedelta):
+        raise ValueError('to_timestring argument must be a datetime.timedelta instance')
+
+    chunks = []
+    microseconds = value.days * 24 * 3600 * 1000000 + value.seconds * 1000000 + value.microseconds
+    for k in values_in_microseconds:
+        if microseconds >= values_in_microseconds[k]:
+            diff, microseconds = divmod(microseconds, values_in_microseconds[k])
+            chunks.append('%d%s' % (diff, k))
+    return u' '.join(chunks)
