Skip to content

Commit

Permalink
Allow format duration as ISO-8601
Browse files Browse the repository at this point in the history
  • Loading branch information
sevdog committed Jun 5, 2024
1 parent 4ef3934 commit 9713c1d
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 4 deletions.
2 changes: 1 addition & 1 deletion docs/api-guide/fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ The `validated_data` for these fields will contain a `datetime.timedelta` instan
* `min_value` Validate that the duration provided is no less than this value.

#### `DurationField` format strings
Format strings may either be the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style intervals should be used (eg `'P4DT1H15M20S'`), or the special string `'standard'`, which indicates that Django interval format `'[DD] [HH:[MM:]]ss[.uuuuuu]'` hould be used (eg: `'4 1:15:20'`).
Format strings may either be the special string `'iso-8601'`, which indicates that [ISO 8601][iso8601] style intervals should be used (eg `'P4DT1H15M20S'`), or the special string `'standard'`, which indicates that Django interval format `'[DD] [HH:[MM:]]ss[.uuuuuu]'` should be used (eg: `'4 1:15:20'`).

---

Expand Down
1 change: 1 addition & 0 deletions rest_framework/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

# Default datetime input and output formats
ISO_8601 = 'iso-8601'
STD_DURATION = 'standard'


class RemovedInDRF316Warning(DeprecationWarning):
Expand Down
13 changes: 11 additions & 2 deletions rest_framework/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from django.utils.dateparse import (
parse_date, parse_datetime, parse_duration, parse_time
)
from django.utils.duration import duration_string
from django.utils.duration import duration_iso_string, duration_string
from django.utils.encoding import is_protected_type, smart_str
from django.utils.formats import localize_input, sanitize_separators
from django.utils.ipv6 import clean_ipv6_address
Expand Down Expand Up @@ -1351,9 +1351,11 @@ class DurationField(Field):
'overflow': _('The number of days must be between {min_days} and {max_days}.'),
}

def __init__(self, **kwargs):
def __init__(self, format=empty, **kwargs):
self.max_value = kwargs.pop('max_value', None)
self.min_value = kwargs.pop('min_value', None)
if format is not empty:
self.format = format
super().__init__(**kwargs)
if self.max_value is not None:
message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
Expand All @@ -1376,6 +1378,13 @@ def to_internal_value(self, value):
self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')

def to_representation(self, value):
output_format = getattr(self, 'format', api_settings.DURATION_FORMAT)

if output_format is None or isinstance(value, str):
return value

if output_format.lower() == ISO_8601:
return duration_iso_string(value)
return duration_string(value)


Expand Down
4 changes: 3 additions & 1 deletion rest_framework/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from django.core.signals import setting_changed
from django.utils.module_loading import import_string

from rest_framework import ISO_8601
from rest_framework import ISO_8601, STD_DURATION

DEFAULTS = {
# Base API policies
Expand Down Expand Up @@ -109,6 +109,8 @@
'TIME_FORMAT': ISO_8601,
'TIME_INPUT_FORMATS': [ISO_8601],

'DURATION_FORMAT': STD_DURATION,

# Encoding
'UNICODE_JSON': True,
'COMPACT_JSON': True,
Expand Down
23 changes: 23 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,29 @@ class TestDurationField(FieldValues):
field = serializers.DurationField()


class TestNoOutputFormatDurationField(FieldValues):
"""
Values for `TimeField` with a no output format.
"""
valid_inputs = {}
invalid_inputs = {}
outputs = {
datetime.timedelta(1): datetime.timedelta(1)
}
field = serializers.DurationField(format=None)


class TestISOOutputFormatDurationField(FieldValues):
"""
Values for `TimeField` with a custom output format.
"""
valid_inputs = {}
invalid_inputs = {}
outputs = {
datetime.timedelta(days=3, hours=8, minutes=32, seconds=1, microseconds=123) : 'P3DT8H32M1S123MS'
}
field = serializers.TimeField(format='is0-8601')

# Choice types...

class TestChoiceField(FieldValues):
Expand Down

0 comments on commit 9713c1d

Please sign in to comment.