#36747 closed New feature (fixed)

parse_duration() fails to parse valid ISO-8601 durations including years, months, and weeks due to incorrect regex

Reported by: florianvazelle Owned by: Varun Kasyap Pentamaraju
Component: Utilities Version: dev
Severity: Normal Keywords:
Cc: Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by florianvazelle)

Description

django.utils.dateparse.parse_duration() claims to support ISO-8601 duration strings, but the current implementation only handles the PnDTnHnMnS subset. Valid ISO-8601 components such as years (Y), months (M), and weeks (W) are not supported.

The internal regex used for ISO-8601 durations:

iso8601_duration_re = _lazy_re_compile(
    r"^(?P<sign>[-+]?)"
    r"P"
    r"(?:(?P<days>\d+([.,]\d+)?)D)?"
    r"(?:T"
    r"(?:(?P<hours>\d+([.,]\d+)?)H)?"
    r"(?:(?P<minutes>\d+([.,]\d+)?)M)?"
    r"(?:(?P<seconds>\d+([.,]\d+)?)S)?"
    r")?"
    r"$"
)

This means Django currently rejects valid duration strings such as:

P1Y2M
P3W
P1Y
P2M10DT2H

Despite the documentation suggesting ISO-8601 support, these forms cannot be parsed.

Steps to Reproduce

from django.utils.dateparse import parse_duration

parse_duration("P1Y")      # returns None
parse_duration("P2M")      # returns None
parse_duration("P3W")      # returns None
parse_duration("P1Y2M3DT4H")  # returns None

Expected Behavior

parse_duration() should parse all valid ISO-8601 durations that can be represented as timedelta, including:

  • PnW
  • PnYnMnD
  • ...

Django should capture each calendar unit, or clearly state limitations.

Change History (8)

comment:1 by florianvazelle, 11 months ago

Description: modified (diff)

comment:2 by David, 11 months ago

This is a limit of supporting Python's timedelta which does not support years and months: ​https://docs.python.org/3/library/datetime.html#timedelta-objects.
Yet it supports weeks, thus the parsing in django could be improved to also support ISO8601 duration with weeks.

If you need to support every IS08601 duration you can use ​https://github.com/CodeYellowBV/django-relativedelta which offers support for relativedelta which implements a more complete timedelta class.

comment:3 by Jacob Walls, 11 months ago

Component: Uncategorized → Utilities
Triage Stage: Unreviewed → Accepted
Type: Bug → Cleanup/optimization
Version: 5.2 → dev

Very helpful triage, David. I'd accept a PR that added support for weeks and documented that years and months are not supported.

comment:4 by Varun Kasyap Pentamaraju, 11 months ago

Owner: set to Varun Kasyap Pentamaraju
Status: new → assigned

comment:5 by Varun Kasyap Pentamaraju, 11 months ago

Has patch: set
Last edited 10 months ago by Varun Kasyap Pentamaraju (previous) (diff)

comment:6 by Jacob Walls, 10 months ago

Needs documentation: set
Type: Cleanup/optimization → New feature

comment:7 by Jacob Walls, 10 months ago

Needs documentation: unset
Triage Stage: Accepted → Ready for checkin

comment:8 by Jacob Walls <jacobtylerwalls@…>, 10 months ago

Resolution: → fixed
Status: assigned → closed

In 0d8548e5:

Fixed #36747 -- Parsed weeks from ISO 8601 format in parse_duration().

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