python-dateutil review
python-dateutil 2.9.0.post0 extends the standard `datetime` module in four distinct areas: permissive string parsing, calendar-relative arithmetic, iCalendar recurrence rules, and additional timezone implementations. `parser.isoparse` handles ISO input, while `parser.parse` accepts much looser text. `relativedelta` moves by calendar months and years instead of fixed seconds. Version 2.9.0 updates bundled timezone data to 2024a, lazily exposes submodules on modern Python, and removes a Python 3.12-deprecated call; post0 fixes generated version-file compatibility.
python-dateutil 2.9.0.post0 installed in 0.2 seconds as 2 packages using 1 MB, with a 0.02-second import and 0 audit findings in our sandbox; keep it for recurrence rules, calendar deltas, or deliberately flexible parsing. Standard-library ISO parsing and zoneinfo are better defaults for narrow modern contracts.
We installed it
| Install | ✓ · 0.2s | 2 packages on disk · 1 MB |
| Import | ✓ | import dateutil in 0.02s · pure Python · requires Python !=3.0.*,!=3.1.*,!=3.2.*,>=2.7 |
| Known vulns | 0 | (pip-audit) |
Answers from our run
Does python-dateutil install cleanly?
Yes. In a fresh container with an empty cache, pip install python-dateutil finished in 0.2s, leaving 2 packages and 1 MB on disk. pip-audit reported no known vulnerabilities.
What does python-dateutil need to run?
Python !=3.0.,!=3.1.,!=3.2.*,>=2.7, and nothing compiled: it is pure Python. In our run import dateutil succeeded in 0.02s.
python-dateutil or pendulum: which should you use?
pendulum: Choose it for its own timezone-aware datetime type, duration API, and opinionated higher-level helpers. python-dateutil 2.9.0.post0 installed in 0.2 seconds as 2 packages using 1 MB, with a 0.02-second import and 0 audit findings in our sandbox; keep it for recurrence rules, calendar deltas, or deliberately flexible parsing.
When should you not use python-dateutil?
The input contract is ISO 8601 supported by datetime.fromisoformat; the standard library is narrower and adds no dependency.
Discussed on
- hnCode that breaks 2 or 3 times every month45 points
Use it if
- Several documented date formats must be accepted and `datetime.fromisoformat` cannot parse all of them.
- Month, year, weekday, or month-end arithmetic must follow calendar rules rather than elapsed seconds.
- The application consumes or generates RFC 5545-style recurrence rules with rrule or rrulestr.
- Existing code already exchanges dateutil tzinfo, parser, or relativedelta objects across package boundaries.
- The input contract is ISO 8601 supported by `datetime.fromisoformat`; the standard library is narrower and adds no dependency.
- Ambiguous dates such as 03/04/05 cannot be tied to an explicit dayfirst and yearfirst policy. Flexible parsing cannot infer user intent safely.
- Hostile arbitrary strings reach parser.parse in a high-volume service. Its permissive search and fallback behavior are a poor validation boundary.
- New timezone code only needs IANA zones available to the runtime. `zoneinfo` provides the standard-library path without dateutil's extra tz API.
- A project requires bundled typing metadata. Our 2.9.0.post0 wheel did not contain py.typed despite the package's wide runtime use.
Setup reality
We installed python-dateutil 2.9.0.post0 in a fresh Python 3.12 Bookworm sandbox in 0.2 seconds. It left 2 packages using 1 MB, and pip-audit reported 0 known vulnerabilities. import dateutil took 0.02 seconds. The distribution is pure Python, declares 1 direct dependency, and reports a dual license. It has no py.typed marker. Package metadata still permits Python 2.7 while excluding Python 3.0 through 3.2.
Prefer datetime.fromisoformat or parser.isoparse for a fixed wire format. General parser.parse fills omitted parts from a default datetime, so an absent day or time can inherit a value the caller never supplied. Pass an explicit default, keep fuzzy parsing off unless required, and define dayfirst plus yearfirst rules for numeric input. Version 2.9.0's lazy submodule imports change convenience, not parsing policy.
Timezone abbreviations are ambiguous. Supply a restricted tzinfos mapping and turn UnknownTimezoneWarning into an error when aware results are mandatory. Parsing text without an offset still returns a naive datetime. Version 2.9.0 bundles tzdata 2024a, which is now older than many system databases; standard-library zoneinfo can use the deployment's current IANA data for new internal code.
relativedelta applies absolute and relative pieces in a defined order, then applies weekday selection. Test January 31, leap days, and daylight-saving transitions. Recurrence iterators may be unbounded; external rules need a count, an until date, or an application-side limit. RFC recurrence skips invalid dates rather than applying relativedelta's month-end clamping, so the two APIs can produce different schedules from similar-looking requirements.
Patterns
Parse a defined ISO value parse-iso-timestamp
from dateutil.parser import isoparse
created_at = isoparse('2026-08-26T14:30:00+05:30')Use isoparse for ISO input. The general parser accepts many extra forms that a wire protocol may not permit.
Parse a loose date with a fixed default parse-known-text
from datetime import datetime
from dateutil.parser import parse
default = datetime(2000, 1, 1, 0, 0, 0)
value = parse('4th of July 2026', default=default)Missing components come from `default`; set it explicitly so today's date or an inherited time cannot leak into the result.
Choose day-first parsing set-numeric-date-order
from dateutil.parser import parse
value = parse('10/09/2026', dayfirst=True, yearfirst=False)dayfirst and yearfirst are application policy. They do not detect the user's locale from the string.
Resolve approved abbreviations map-timezone-name
from dateutil import parser, tz
zones = {
'IST': tz.gettz('Asia/Kolkata'),
'CST': tz.gettz('America/Chicago'),
}
value = parser.parse('2026-08-26 14:30 IST', tzinfos=zones)Abbreviations such as IST and CST have multiple meanings, so accept only mappings defined by the application.
Move from month end add-calendar-month
from datetime import date
from dateutil.relativedelta import relativedelta
result = date(2026, 1, 31) + relativedelta(months=1)
# 2026-02-28relativedelta clamps when the corresponding day does not exist; it does not treat one month as a fixed duration.
Get the final Friday find-month-weekday
from datetime import date
from dateutil.relativedelta import FR, relativedelta
last_friday = date(2026, 8, 1) + relativedelta(
day=31, weekday=FR(-1)
)The absolute day is applied before the weekday adjustment, following relativedelta's documented component order.
Express a difference in calendar units measure-calendar-gap
from datetime import date
from dateutil.relativedelta import relativedelta
gap = relativedelta(date(2026, 8, 26), date(2020, 2, 29))
print(gap.years, gap.months, gap.days)Calendar years, months, and days do not represent a fixed number of elapsed seconds.
List four weekly occurrences generate-bounded-recurrence
from datetime import datetime
from dateutil.rrule import MO, WE, WEEKLY, rrule
occurrences = list(rrule(
WEEKLY,
byweekday=(MO, WE),
count=4,
dtstart=datetime(2026, 8, 24),
))Always bound external recurrence rules with count, until, or an application limit before materializing them.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| pendulum | PyPI | Choose it for its own timezone-aware datetime type, duration API, and opinionated higher-level helpers. |
| dateparser | PyPI | Choose it for multilingual month names and relative natural-language phrases such as dates expressed in human terms. |
| arrow | PyPI | Choose it when a chainable wrapper and human-readable formatting are more important than RFC recurrence rules. |
More utils guides
lru-cache · type-fest · ajv · p-limit · find-up · js-yaml · the whole shelf →
How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.

