Opened 9 years ago

Closed 4 years ago

#25060 closed New feature (fixed)

Add support for str(timedelta) representation in parse_duration

Reported by: Mikhail Owned by: nobody
Component: Utilities Version: dev
Severity: Normal Keywords:
Cc: Mikhail, Marc Tamlyn Triage Stage: Accepted
Has patch: yes Needs documentation: yes
Needs tests: yes Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

It's useful for conversions to/from str, so we can convert timedelta value to str and back without additional work.
The following code can be used instead of original parse_duration function

# Support str(timedelta) format
str_timedelta_duration_re = re.compile(
    r'^'
    r'(?:(?P<days>-?\d+) days*, )?'
    r'((?:(?P<hours>\d+):)(?=\d+:\d+))?'
    r'(?:(?P<minutes>\d+):)?'
    r'(?P<seconds>\d+)'
    r'(?:\.(?P<microseconds>\d{1,6})\d{0,6})?'
    r'$'
)

def parse_duration(value):
    """Parses a duration string and returns a datetime.timedelta.

    The preferred format for durations in Django is '%d %H:%M:%S.%f'.

    Also supports ISO 8601 and str(timedelta) representations.
    """
    for duration_re in (standard_duration_re, iso8601_duration_re, str_timedelta_duration_re):
        match = duration_re.match(value)
        if match:
            kw = match.groupdict()
            if kw.get('microseconds'):
                kw['microseconds'] = kw['microseconds'].ljust(6, '0')
            kw = {k: float(v) for k, v in six.iteritems(kw) if v is not None}
            return datetime.timedelta(**kw)

Change History (10)

comment:1 by Tim Graham, 9 years ago

Can you elaborate on your use case a bit?

parse_duration() is currently the inverse of django.utils.duration.duration_string() which is not English specific. Maybe you can use that function instead of str().

comment:2 by Mikhail, 9 years ago

Cc: Mikhail added

My specific use case is DurationField in django-hstore schema mode (but I suppose it can be useful in other situations).

Without this patch to_python() raise an error about inappropriate format. I agree that in my use case it's preferable to use duration_string() instead of str() for conversion, but I still didn't find the place where I can override this behavior on django-hstore side :).

Seems now I understand why you don't implement it, str(timedelta) output can be locale dependent. If so this patch is controversial point.

Last edited 7 years ago by Tim Graham (previous) (diff)

comment:4 by Tim Graham, 9 years ago

Have you raised the issue with django-hstore? I wonder if it should be calling the form field prepare_value() method?

comment:5 by Mikhail, 9 years ago

For now - no. DurationField is not in the list of officially supported field types, docs say that some other fields can work as well but some of them need additional work. Seems for other types str() method is called by default. So DurationField support is just new feature for django-hstore. I'm working on support of additional fields but not as the part of django.

I've put parse_duration() patch here because I thought it can be useful for django core itself. English specificity can be avoided using more versatile regex like:

str_timedelta_duration_re = re.compile(
    r'^'
    r'(?:(?P<days>-?\d+) \w+, )?'
    r'((?:(?P<hours>\d+):)(?=\d+:\d+))?'
    r'(?:(?P<minutes>\d+):)?'
    r'(?P<seconds>\d+)'
    r'(?:\.(?P<microseconds>\d{1,6})\d{0,6})?'
    r'$'
)

comment:6 by Tim Graham, 9 years ago

Cc: Marc Tamlyn added

Marc, your thoughts on this?

comment:7 by Marc Tamlyn, 9 years ago

I can see the merit in supporting the the str(timedelta) format in English. The intention was to encourage locale independent representation of the timedelta. I am completely unfamiliar with what str(timedelta) does in other locales, if we could support it generically that would be great.

comment:8 by Mikhail, 9 years ago

Seems str(timedelta) output is locale independent. I've checked on several locales and all the time output is the same.

>>> for loc in ('ar_AE', 'ar_BH', 'ar_DZ', 'ar_EG', 'ar_IN', 'ar_IQ', 'ar_JO', 'ar_KW', 'ar_LB', 'ar_LY', 'ar_MA', 'ar_OM', 'ar_QA', 'ar_SA', 'ar_SD', 'ar_SY', 'ar_TN', 'ar_YE', 'C', 'de_AT', 'de_BE', 'de_CH', 'de_DE', 'de_LI', 'de_LU', 'en_AG', 'en_AU', 'en_BW', 'en_CA', 'en_DK', 'en_GB', 'en_HK', 'en_IE', 'en_IN', 'en_NG', 'en_NZ', 'en_PH', 'en_SG', 'en_US', 'en_ZA', 'en_ZM', 'en_ZW', 'it_CH', 'it_IT', 'ja_JP', 'ko_KR', 'mr_IN', 'POSIX', 'ru_RU', 'ru_UA', 'sa_IN', 'tr_CY', 'tr_TR', 'zh_HK', 'zh_TW', 'zu_ZA', ):
...     locale.setlocale(locale.LC_ALL, (loc, 'utf-8'))
...     str(timedelta(777, 0, 1))
... 
'ar_AE.UTF-8'
'777 days, 0:00:00.000001'
'ar_BH.UTF-8'
'777 days, 0:00:00.000001'
etc....

Also there are some topics in web about formating str(timedelta) according to locale, it shows str(timedelta) is locale independent. So, I suppose, we can use English regex.

comment:9 by Tim Graham, 9 years ago

Easy pickings: unset
Needs documentation: set
Needs tests: set
Triage Stage: UnreviewedAccepted
Version: 1.8master

The patch also requires tests and documentation updates. See also the patch review checklist.

comment:10 by Matthew Schinckel, 7 years ago

I think the parse_duration() function already does this.

in reply to:  10 comment:11 by Baptiste Mispelon, 4 years ago

Resolution: fixed
Status: newclosed

Replying to Matthew Schinckel:

I think the parse_duration() function already does this.

That's correct. It looks like this was inadvertently added with #24897 (262d4db8c4c849b0fdd84550fb96472446cf90df).

There are even tests for a variety of different timedelta cases: https://github.com/django/django/blob/a5855c8f0fe17b7e888bd8137874ef78012a7294/tests/utils_tests/test_dateparse.py#L54

Note: See TracTickets for help on using tickets.
Back to Top