mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-08-05 13:00:12 +03:00
Merge f2cb74edde
into d6c6df40c6
This commit is contained in:
commit
cfb21f4ae7
|
@ -299,7 +299,7 @@ Corresponds to `django.db.models.fields.DateTimeField`.
|
||||||
|
|
||||||
#### `DateTimeField` format strings.
|
#### `DateTimeField` format strings.
|
||||||
|
|
||||||
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style datetimes should be used. (eg `'2013-01-29T12:34:56.000000Z'`)
|
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special strings `'iso-8601'`, which indicates that [ISO 8601][iso8601] style datetimes should be used (eg `'2013-01-29T12:34:56.000000Z'`), and `'iso-8601-strict'`, which indicates that [ISO 8601][iso8601] style datetimes without fraction seconds should be used (eg `'2013-01-29T12:34:56Z'`)
|
||||||
|
|
||||||
When a value of `None` is used for the format `datetime` objects will be returned by `to_representation` and the final output representation will determined by the renderer class.
|
When a value of `None` is used for the format `datetime` objects will be returned by `to_representation` and the final output representation will determined by the renderer class.
|
||||||
|
|
||||||
|
@ -345,7 +345,7 @@ Corresponds to `django.db.models.fields.TimeField`
|
||||||
|
|
||||||
#### `TimeField` format strings
|
#### `TimeField` format strings
|
||||||
|
|
||||||
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style times should be used. (eg `'12:34:56.000000'`)
|
Format strings may either be [Python strftime formats][strftime] which explicitly specify the format, or the special strings `'iso-8601'`, which indicates that [ISO 8601][iso8601] style times should be used (eg `'12:34:56.000000'`), and `'iso-8601-strict'`, which indicates that [ISO 8601][iso8601] style times without fractional seconds should be used (eg `'12:34:56'`).
|
||||||
|
|
||||||
## DurationField
|
## DurationField
|
||||||
|
|
||||||
|
|
|
@ -286,7 +286,7 @@ Default: `'format'`
|
||||||
|
|
||||||
A format string that should be used by default for rendering the output of `DateTimeField` serializer fields. If `None`, then `DateTimeField` serializer fields will return Python `datetime` objects, and the datetime encoding will be determined by the renderer.
|
A format string that should be used by default for rendering the output of `DateTimeField` serializer fields. If `None`, then `DateTimeField` serializer fields will return Python `datetime` objects, and the datetime encoding will be determined by the renderer.
|
||||||
|
|
||||||
May be any of `None`, `'iso-8601'` or a Python [strftime format][strftime] string.
|
May be any of `None`, `'iso-8601'`, `'iso-8601-strict'`, or a Python [strftime format][strftime] string.
|
||||||
|
|
||||||
Default: `'iso-8601'`
|
Default: `'iso-8601'`
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ Default: `'iso-8601'`
|
||||||
|
|
||||||
A list of format strings that should be used by default for parsing inputs to `DateTimeField` serializer fields.
|
A list of format strings that should be used by default for parsing inputs to `DateTimeField` serializer fields.
|
||||||
|
|
||||||
May be a list including the string `'iso-8601'` or Python [strftime format][strftime] strings.
|
May be a list including the string `'iso-8601'`, `'iso-8601-strict'` or Python [strftime format][strftime] strings.
|
||||||
|
|
||||||
Default: `['iso-8601']`
|
Default: `['iso-8601']`
|
||||||
|
|
||||||
|
@ -318,7 +318,7 @@ Default: `['iso-8601']`
|
||||||
|
|
||||||
A format string that should be used by default for rendering the output of `TimeField` serializer fields. If `None`, then `TimeField` serializer fields will return Python `time` objects, and the time encoding will be determined by the renderer.
|
A format string that should be used by default for rendering the output of `TimeField` serializer fields. If `None`, then `TimeField` serializer fields will return Python `time` objects, and the time encoding will be determined by the renderer.
|
||||||
|
|
||||||
May be any of `None`, `'iso-8601'` or a Python [strftime format][strftime] string.
|
May be any of `None`, `'iso-8601'`, `'iso-8601-strict'` or a Python [strftime format][strftime] string.
|
||||||
|
|
||||||
Default: `'iso-8601'`
|
Default: `'iso-8601'`
|
||||||
|
|
||||||
|
@ -326,7 +326,7 @@ Default: `'iso-8601'`
|
||||||
|
|
||||||
A list of format strings that should be used by default for parsing inputs to `TimeField` serializer fields.
|
A list of format strings that should be used by default for parsing inputs to `TimeField` serializer fields.
|
||||||
|
|
||||||
May be a list including the string `'iso-8601'` or Python [strftime format][strftime] strings.
|
May be a list including the string `'iso-8601'`, `'iso-8601-strict'` or Python [strftime format][strftime] strings.
|
||||||
|
|
||||||
Default: `['iso-8601']`
|
Default: `['iso-8601']`
|
||||||
|
|
||||||
|
|
|
@ -21,3 +21,4 @@ HTTP_HEADER_ENCODING = 'iso-8859-1'
|
||||||
|
|
||||||
# Default datetime input and output formats
|
# Default datetime input and output formats
|
||||||
ISO_8601 = 'iso-8601'
|
ISO_8601 = 'iso-8601'
|
||||||
|
ISO_8601_STRICT = 'iso-8601-strict'
|
||||||
|
|
|
@ -30,7 +30,7 @@ from django.utils.functional import cached_property
|
||||||
from django.utils.ipv6 import clean_ipv6_address
|
from django.utils.ipv6 import clean_ipv6_address
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import ISO_8601
|
from rest_framework import ISO_8601, ISO_8601_STRICT
|
||||||
from rest_framework.compat import (
|
from rest_framework.compat import (
|
||||||
get_remote_field, unicode_repr, unicode_to_repr, value_from_object
|
get_remote_field, unicode_repr, unicode_to_repr, value_from_object
|
||||||
)
|
)
|
||||||
|
@ -1120,13 +1120,15 @@ class DateTimeField(Field):
|
||||||
return self.enforce_timezone(value)
|
return self.enforce_timezone(value)
|
||||||
|
|
||||||
for input_format in input_formats:
|
for input_format in input_formats:
|
||||||
if input_format.lower() == ISO_8601:
|
if input_format.lower() in (ISO_8601, ISO_8601_STRICT):
|
||||||
try:
|
try:
|
||||||
parsed = parse_datetime(value)
|
parsed = parse_datetime(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
|
if input_format == ISO_8601_STRICT:
|
||||||
|
parsed = parsed - datetime.timedelta(microseconds=parsed.microsecond)
|
||||||
return self.enforce_timezone(parsed)
|
return self.enforce_timezone(parsed)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -1153,6 +1155,10 @@ class DateTimeField(Field):
|
||||||
if value.endswith('+00:00'):
|
if value.endswith('+00:00'):
|
||||||
value = value[:-6] + 'Z'
|
value = value[:-6] + 'Z'
|
||||||
return value
|
return value
|
||||||
|
if output_format.lower() == ISO_8601_STRICT:
|
||||||
|
value = value - datetime.timedelta(microseconds=value.microsecond)
|
||||||
|
return value.isoformat()
|
||||||
|
|
||||||
return value.strftime(output_format)
|
return value.strftime(output_format)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1243,13 +1249,15 @@ class TimeField(Field):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
for input_format in input_formats:
|
for input_format in input_formats:
|
||||||
if input_format.lower() == ISO_8601:
|
if input_format.lower() in (ISO_8601, ISO_8601_STRICT):
|
||||||
try:
|
try:
|
||||||
parsed = parse_time(value)
|
parsed = parse_time(value)
|
||||||
except (ValueError, TypeError):
|
except (ValueError, TypeError):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
|
if input_format.lower() == ISO_8601_STRICT:
|
||||||
|
return parsed.replace(microsecond=0)
|
||||||
return parsed
|
return parsed
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
|
@ -1282,6 +1290,10 @@ class TimeField(Field):
|
||||||
|
|
||||||
if output_format.lower() == ISO_8601:
|
if output_format.lower() == ISO_8601:
|
||||||
return value.isoformat()
|
return value.isoformat()
|
||||||
|
if output_format.lower() == ISO_8601_STRICT:
|
||||||
|
if value.microsecond:
|
||||||
|
value = value.replace(microsecond=0)
|
||||||
|
return value.isoformat()
|
||||||
return value.strftime(output_format)
|
return value.strftime(output_format)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
"""
|
"""
|
||||||
Helper functions that convert strftime formats into more readable representations.
|
Helper functions that convert strftime formats into more readable representations.
|
||||||
"""
|
"""
|
||||||
from rest_framework import ISO_8601
|
from rest_framework import ISO_8601, ISO_8601_STRICT
|
||||||
|
|
||||||
|
|
||||||
def datetime_formats(formats):
|
def datetime_formats(formats):
|
||||||
format = ', '.join(formats).replace(
|
format = ', '.join(formats).replace(
|
||||||
|
ISO_8601_STRICT,
|
||||||
|
'YYYY-MM-DDThh:mm[:ss][+HH:MM|-HH:MM|Z]'
|
||||||
|
).replace(
|
||||||
ISO_8601,
|
ISO_8601,
|
||||||
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]'
|
'YYYY-MM-DDThh:mm[:ss[.uuuuuu]][+HH:MM|-HH:MM|Z]'
|
||||||
)
|
)
|
||||||
|
@ -18,7 +21,8 @@ def date_formats(formats):
|
||||||
|
|
||||||
|
|
||||||
def time_formats(formats):
|
def time_formats(formats):
|
||||||
format = ', '.join(formats).replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]')
|
format = ', '.join(formats).replace(ISO_8601_STRICT, 'hh:mm[:ss]') \
|
||||||
|
.replace(ISO_8601, 'hh:mm[:ss[.uuuuuu]]')
|
||||||
return humanize_strptime(format)
|
return humanize_strptime(format)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1130,10 +1130,15 @@ class TestDateTimeField(FieldValues):
|
||||||
"""
|
"""
|
||||||
valid_inputs = {
|
valid_inputs = {
|
||||||
'2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
'2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01 13:00:00.123456': datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
'2001-01-01T13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
'2001-01-01T13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01T13:00:00.123456': datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01T13:00:00.123456Z': datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456): datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
# Django 1.4 does not support timezone string parsing.
|
# Django 1.4 does not support timezone string parsing.
|
||||||
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC())
|
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC())
|
||||||
}
|
}
|
||||||
|
@ -1144,7 +1149,9 @@ class TestDateTimeField(FieldValues):
|
||||||
}
|
}
|
||||||
outputs = {
|
outputs = {
|
||||||
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00',
|
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00',
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456): '2001-01-01T13:00:00.123456',
|
||||||
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): '2001-01-01T13:00:00Z',
|
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): '2001-01-01T13:00:00Z',
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()): '2001-01-01T13:00:00.123456Z',
|
||||||
'2001-01-01T00:00:00': '2001-01-01T00:00:00',
|
'2001-01-01T00:00:00': '2001-01-01T00:00:00',
|
||||||
six.text_type('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
|
six.text_type('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
|
||||||
None: None,
|
None: None,
|
||||||
|
@ -1153,6 +1160,41 @@ class TestDateTimeField(FieldValues):
|
||||||
field = serializers.DateTimeField(default_timezone=timezone.UTC())
|
field = serializers.DateTimeField(default_timezone=timezone.UTC())
|
||||||
|
|
||||||
|
|
||||||
|
class TestIso8601StrictDateTimeField(FieldValues):
|
||||||
|
valid_inputs = {
|
||||||
|
'2001-01-01 13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01 13:00:00.123456': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01T13:00': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01T13:00:00.123456': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
'2001-01-01T13:00:00.123456Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456): datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()),
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()): datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()),
|
||||||
|
# Django 1.4 does not support timezone string parsing.
|
||||||
|
'2001-01-01T13:00Z': datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC())
|
||||||
|
}
|
||||||
|
invalid_inputs = {
|
||||||
|
'abc': ['Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss][+HH:MM|-HH:MM|Z].'],
|
||||||
|
'2001-99-99T99:00': ['Datetime has wrong format. Use one of these formats instead: YYYY-MM-DDThh:mm[:ss][+HH:MM|-HH:MM|Z].'],
|
||||||
|
datetime.date(2001, 1, 1): ['Expected a datetime but got a date.'],
|
||||||
|
}
|
||||||
|
outputs = {
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00): '2001-01-01T13:00:00',
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456): '2001-01-01T13:00:00',
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, tzinfo=timezone.UTC()): '2001-01-01T13:00:00+00:00',
|
||||||
|
datetime.datetime(2001, 1, 1, 13, 00, 00, 123456, tzinfo=timezone.UTC()): '2001-01-01T13:00:00+00:00',
|
||||||
|
'2001-01-01T00:00:00': '2001-01-01T00:00:00',
|
||||||
|
six.text_type('2016-01-10T00:00:00'): '2016-01-10T00:00:00',
|
||||||
|
None: None,
|
||||||
|
'': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
field = serializers.DateTimeField(format=rest_framework.ISO_8601_STRICT, input_formats=[rest_framework.ISO_8601_STRICT],
|
||||||
|
default_timezone=timezone.UTC())
|
||||||
|
|
||||||
|
|
||||||
class TestCustomInputFormatDateTimeField(FieldValues):
|
class TestCustomInputFormatDateTimeField(FieldValues):
|
||||||
"""
|
"""
|
||||||
Valid and invalid values for `DateTimeField` with a custom input format.
|
Valid and invalid values for `DateTimeField` with a custom input format.
|
||||||
|
@ -1210,7 +1252,9 @@ class TestTimeField(FieldValues):
|
||||||
"""
|
"""
|
||||||
valid_inputs = {
|
valid_inputs = {
|
||||||
'13:00': datetime.time(13, 00),
|
'13:00': datetime.time(13, 00),
|
||||||
|
'13:00:00.123456': datetime.time(13, 00, 00, 123456),
|
||||||
datetime.time(13, 00): datetime.time(13, 00),
|
datetime.time(13, 00): datetime.time(13, 00),
|
||||||
|
datetime.time(13, 00, 00, 123456): datetime.time(13, 00, 00, 123456),
|
||||||
}
|
}
|
||||||
invalid_inputs = {
|
invalid_inputs = {
|
||||||
'abc': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].'],
|
'abc': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss[.uuuuuu]].'],
|
||||||
|
@ -1218,14 +1262,44 @@ class TestTimeField(FieldValues):
|
||||||
}
|
}
|
||||||
outputs = {
|
outputs = {
|
||||||
datetime.time(13, 0): '13:00:00',
|
datetime.time(13, 0): '13:00:00',
|
||||||
|
datetime.time(13, 0, 0, 123456): '13:00:00.123456',
|
||||||
datetime.time(0, 0): '00:00:00',
|
datetime.time(0, 0): '00:00:00',
|
||||||
|
datetime.time(0, 0, 0, 0): '00:00:00',
|
||||||
'00:00:00': '00:00:00',
|
'00:00:00': '00:00:00',
|
||||||
|
'00:00:00.000000': '00:00:00.000000',
|
||||||
None: None,
|
None: None,
|
||||||
'': None,
|
'': None,
|
||||||
}
|
}
|
||||||
field = serializers.TimeField()
|
field = serializers.TimeField()
|
||||||
|
|
||||||
|
|
||||||
|
class TestIso8601StrictTimeField(FieldValues):
|
||||||
|
"""
|
||||||
|
Valid and invalid values for `TimeField`.
|
||||||
|
"""
|
||||||
|
valid_inputs = {
|
||||||
|
'13:00': datetime.time(13, 00),
|
||||||
|
'13:00:00.123456': datetime.time(13, 00),
|
||||||
|
datetime.time(13, 00): datetime.time(13, 00),
|
||||||
|
datetime.time(13, 00, 00, 123456): datetime.time(13, 00, 00, 123456),
|
||||||
|
}
|
||||||
|
invalid_inputs = {
|
||||||
|
'abc': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss].'],
|
||||||
|
'99:99': ['Time has wrong format. Use one of these formats instead: hh:mm[:ss].'],
|
||||||
|
}
|
||||||
|
outputs = {
|
||||||
|
datetime.time(13, 0): '13:00:00',
|
||||||
|
datetime.time(13, 0, 0, 123456): '13:00:00',
|
||||||
|
datetime.time(0, 0): '00:00:00',
|
||||||
|
datetime.time(0, 0, 0, 0): '00:00:00',
|
||||||
|
'00:00:00': '00:00:00',
|
||||||
|
'00:00:00.000000': '00:00:00.000000',
|
||||||
|
None: None,
|
||||||
|
'': None,
|
||||||
|
}
|
||||||
|
field = serializers.TimeField(format=rest_framework.ISO_8601_STRICT, input_formats=[rest_framework.ISO_8601_STRICT])
|
||||||
|
|
||||||
|
|
||||||
class TestCustomInputFormatTimeField(FieldValues):
|
class TestCustomInputFormatTimeField(FieldValues):
|
||||||
"""
|
"""
|
||||||
Valid and invalid values for `TimeField` with a custom input format.
|
Valid and invalid values for `TimeField` with a custom input format.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user