diff --git a/django/forms/fields.py b/django/forms/fields.py
index 7f0d26d..124e4f6 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -199,7 +199,7 @@ class CharField(Field):
 
     def widget_attrs(self, widget):
         attrs = super(CharField, self).widget_attrs(widget)
-        if self.max_length is not None and isinstance(widget, (TextInput, PasswordInput)):
+        if self.max_length is not None and isinstance(widget, TextInput):
             # The HTML attribute is maxlength, not max_length.
             attrs.update({'maxlength': str(self.max_length)})
         return attrs
diff --git a/django/forms/widgets.py b/django/forms/widgets.py
index 7651efc..f8af9e0 100644
--- a/django/forms/widgets.py
+++ b/django/forms/widgets.py
@@ -260,10 +260,17 @@ class Input(Widget):
             final_attrs['value'] = force_text(self._format_value(value))
         return format_html('<input{0} />', flatatt(final_attrs))
 
+
 class TextInput(Input):
     input_type = 'text'
 
-class PasswordInput(Input):
+    def __init__(self, attrs=None, input_type=None):
+        if input_type is not None:
+            self.input_type = input_type
+        super(TextInput, self).__init__(attrs)
+
+
+class PasswordInput(TextInput):
     input_type = 'password'
 
     def __init__(self, attrs=None, render_value=False):
@@ -400,11 +407,10 @@ class Textarea(Widget):
                            flatatt(final_attrs),
                            force_text(value))
 
-class DateInput(Input):
-    input_type = 'text'
 
-    def __init__(self, attrs=None, format=None):
-        super(DateInput, self).__init__(attrs)
+class DateInput(TextInput):
+    def __init__(self, attrs=None, format=None, **kwargs):
+        super(DateInput, self).__init__(attrs, **kwargs)
         if format:
             self.format = format
             self.manual_format = True
@@ -431,11 +437,10 @@ class DateInput(Input):
             pass
         return super(DateInput, self)._has_changed(self._format_value(initial), data)
 
-class DateTimeInput(Input):
-    input_type = 'text'
 
-    def __init__(self, attrs=None, format=None):
-        super(DateTimeInput, self).__init__(attrs)
+class DateTimeInput(TextInput):
+    def __init__(self, attrs=None, format=None, **kwargs):
+        super(DateTimeInput, self).__init__(attrs, **kwargs)
         if format:
             self.format = format
             self.manual_format = True
@@ -462,11 +467,10 @@ class DateTimeInput(Input):
             pass
         return super(DateTimeInput, self)._has_changed(self._format_value(initial), data)
 
-class TimeInput(Input):
-    input_type = 'text'
 
-    def __init__(self, attrs=None, format=None):
-        super(TimeInput, self).__init__(attrs)
+class TimeInput(TextInput):
+    def __init__(self, attrs=None, format=None, **kwargs):
+        super(TimeInput, self).__init__(attrs, **kwargs)
         if format:
             self.format = format
             self.manual_format = True
diff --git a/docs/ref/forms/widgets.txt b/docs/ref/forms/widgets.txt
index eab314a..96187e2 100644
--- a/docs/ref/forms/widgets.txt
+++ b/docs/ref/forms/widgets.txt
@@ -178,6 +178,16 @@ commonly used groups of widgets:
 
     Text input: ``<input type='text' ...>``
 
+    Takes one optional argument:
+
+    .. attribute:: TextInput.input_type
+
+        The content of the type attribute of the input element (defaults to
+        'text').
+
+    .. versionchanged:: 1.5
+        The `input_type` argument has been added
+
 ``PasswordInput``
 ~~~~~~~~~~~~~~~~~
 
@@ -245,7 +255,7 @@ commonly used groups of widgets:
 
     Date input as a simple text box: ``<input type='text' ...>``
 
-    Takes one optional argument:
+    Takes same arguments as :class:`TextInput`, with one more optional argument:
 
     .. attribute:: DateInput.format
 
@@ -262,7 +272,7 @@ commonly used groups of widgets:
 
     Date/time input as a simple text box: ``<input type='text' ...>``
 
-    Takes one optional argument:
+    Takes same arguments as :class:`TextInput`, with one more optional argument:
 
     .. attribute:: DateTimeInput.format
 
@@ -279,7 +289,7 @@ commonly used groups of widgets:
 
     Time input as a simple text box: ``<input type='text' ...>``
 
-    Takes one optional argument:
+    Takes same arguments as :class:`TextInput`, with one more optional argument:
 
     .. attribute:: TimeInput.format
 
diff --git a/tests/regressiontests/forms/tests/widgets.py b/tests/regressiontests/forms/tests/widgets.py
index 544ca41..e062b9f 100644
--- a/tests/regressiontests/forms/tests/widgets.py
+++ b/tests/regressiontests/forms/tests/widgets.py
@@ -34,6 +34,9 @@ class FormsWidgetTestCase(TestCase):
         w = TextInput(attrs={'class': 'fun'})
         self.assertHTMLEqual(w.render('email', ''), '<input type="text" class="fun" name="email" />')
         self.assertHTMLEqual(w.render('email', 'foo@example.com'), '<input type="text" class="fun" value="foo@example.com" name="email" />')
+        # ... or input_type
+        w = TextInput(input_type='hello')
+        self.assertHTMLEqual(w.render('generic', ''), u'<input type="hello" name="generic" />')
 
         # 'attrs' passed to render() get precedence over those passed to the constructor:
         w = TextInput(attrs={'class': 'pretty'})
@@ -919,6 +922,9 @@ beatle J R Ringo False""")
         self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17/09/2007 12:51" />')
         self.assertFalse(w._has_changed(d, '17/09/2007 12:51'))
 
+        w = DateTimeInput(input_type='datetime')
+        self.assertHTMLEqual(w.render('date', None), '<input type="datetime" name="date" />')
+
         # Make sure a custom format works with _has_changed. The hidden input will use
         data = datetime.datetime(2010, 3, 6, 12, 0, 0)
         custom_format = '%d.%m.%Y %H:%M'
@@ -942,6 +948,9 @@ beatle J R Ringo False""")
         self.assertHTMLEqual(w.render('date', d), '<input type="text" name="date" value="17/09/2007" />')
         self.assertFalse(w._has_changed(d, '17/09/2007'))
 
+        w = DateInput(input_type='date')
+        self.assertHTMLEqual(w.render('date', None), '<input type="date" name="date" />')
+
         # Make sure a custom format works with _has_changed. The hidden input will use
         data = datetime.date(2010, 3, 6)
         custom_format = '%d.%m.%Y'
@@ -967,6 +976,9 @@ beatle J R Ringo False""")
         self.assertHTMLEqual(w.render('time', t), '<input type="text" name="time" value="12:51" />')
         self.assertFalse(w._has_changed(t, '12:51'))
 
+        w = DateInput(input_type='time')
+        self.assertHTMLEqual(w.render('time', None), '<input type="time" name="time" />')
+
         # Make sure a custom format works with _has_changed. The hidden input will use
         data = datetime.time(13, 0)
         custom_format = '%I:%M %p'
