Opened 2 years ago

Last modified 5 months ago

#25060 new New feature

Add support for str(timedelta) representation in parse_duration

Reported by: Mikhail Owned by: nobody
Component: Utilities Version: master
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 (9)

comment:1 Changed 2 years ago by Tim Graham

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 Changed 2 years ago by Mikhail

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 6 months ago by Tim Graham (previous) (diff)

comment:4 Changed 2 years ago by Tim Graham

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

comment:5 Changed 2 years ago by Mikhail

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 Changed 2 years ago by Tim Graham

Cc: Marc Tamlyn added

Marc, your thoughts on this?

comment:7 Changed 2 years ago by Marc Tamlyn

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 Changed 2 years ago by Mikhail

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 Changed 2 years ago by Tim Graham

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 Changed 5 months ago by Matthew Schinckel

I think the parse_duration() function already does this.

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