{"id":646,"library":"isoduration","title":"isoduration","description":"isoduration is a Python library for working with ISO 8601 durations (e.g., 'P3Y6M4DT12H30M5S'). It provides functionality for parsing duration strings, formatting durations, and performing arithmetic operations with Python's `datetime` objects. The library is currently at version 20.11.0, released in 2020, and appears to be in maintenance mode with no recent major releases, though minor updates or repackaging may occur.","status":"maintenance","version":"20.11.0","language":"python","source_language":"en","source_url":"https://github.com/bolsote/isoduration","tags":["iso8601","duration","parsing","datetime","timedelta"],"install":[{"cmd":"pip install isoduration","lang":"bash","label":"Install stable version"}],"dependencies":[],"imports":[{"symbol":"parse_duration","correct":"from isoduration import parse_duration"},{"note":"Returned by parse_duration for complex ISO 8601 durations involving years/months.","symbol":"Duration","correct":"from isoduration import Duration"}],"quickstart":{"code":"from datetime import datetime\nfrom isoduration import parse_duration\n\n# Parse an ISO 8601 duration string\nduration_str = \"P3Y6M4DT12H30M5S\"\nduration = parse_duration(duration_str)\n\nprint(f\"Parsed duration: {duration}\")\nprint(f\"Years: {duration.date.years}, Months: {duration.date.months}\")\nprint(f\"Hours: {duration.time.hours}, Seconds: {duration.time.seconds}\")\n\n# Add the duration to a datetime object\nstart_time = datetime(2023, 1, 1, 10, 0, 0)\nend_time = start_time + duration\nprint(f\"Start time: {start_time}\")\nprint(f\"End time after adding duration: {end_time}\")\n\n# Negate a duration\nnegative_duration = -duration\nprint(f\"Negative duration: {negative_duration}\")","lang":"python","description":"This quickstart demonstrates parsing an ISO 8601 duration string into a `Duration` object, accessing its components, and performing arithmetic by adding it to a `datetime` object."},"warnings":[{"fix":"Be aware that `parse_duration` may return a `Duration` object, not always a `timedelta`. The `Duration` object correctly implements addition/subtraction with `datetime` objects.","message":"ISO 8601 durations that include years or months represent variable-length periods (e.g., '1 month' can be 28, 29, 30, or 31 days). Python's `datetime.timedelta` is designed for fixed-length durations. `isoduration` addresses this by returning its own `Duration` object for such cases, which correctly handles arithmetic with `datetime` objects.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Consider its maintenance status when planning for long-term projects or needing new features/bug fixes. It appears stable for its current scope.","message":"The library had its initial and only PyPI release (20.11.0) in November 2020. While the GitHub repository shows some minor activity in 2021, there haven't been subsequent PyPI releases, suggesting limited active development or a stable, feature-complete state.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Access components via `duration.date.years`, `duration.time.hours`, etc. Perform arithmetic with `datetime` objects for correct temporal calculations. Avoid direct `timedelta` conversion if the `Duration` contains years or months, as `timedelta` cannot represent these precisely.","message":"The `Duration` object's internal components (years, months, days, etc.) are stored as `Decimal` types for precision. Direct comparison or conversion to `datetime.timedelta` (especially for years/months) needs careful handling due to the inherent ambiguity of these units.","severity":"gotcha","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T17:13:37.039Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Ensure the duration string adheres to the ISO 8601 format (e.g., 'P3Y6M4DT12H30M5S', 'PT1H30M', 'P1W'). For instance, 'P3X' should be 'P3D' for three days or 'PT3S' for three seconds.","cause":"The input string provided to `parse_duration()` does not strictly conform to the ISO 8601 duration format, often due to typos, missing 'P' or 'T' designators, or incorrect units.","error":"ValueError: Invalid ISO 8601 duration string: 'P3X'"},{"fix":"Identify where `DurationParsingException` is being referenced in your code or a dependent library. Replace it with a more general exception like `ValueError`, or specifically catch `iso8601.iso8601.ParseError` if that's the underlying exception being raised and you have `iso8601` installed as a dependency. If it's a dependency causing the issue, check for compatibility or try reinstalling the packages.","cause":"This error occurs when code attempts to reference `isoduration.DurationParsingException`, which is not an exposed exception in the `isoduration` library (bolsote/isoduration). It might be an internal exception type, or a class from a different, related library like `isodate` or `iso8601`.","error":"AttributeError: module 'isoduration' has no attribute 'DurationParsingException'"},{"fix":"To get total seconds, you need to convert the `Duration` object to a `timedelta` first using `duration.to_timedelta()`. Note that `to_timedelta()` will raise a `ValueError` if the duration contains years or months, as these cannot be unambiguously converted to seconds without a specific context date. For durations with years/months, access the `years`, `months`, `days`, `hours`, `minutes`, `seconds` attributes from `duration.date` and `duration.time` directly.","cause":"The `isoduration` library returns its own `Duration` object, not a standard `datetime.timedelta`, especially when the duration includes years or months. The `total_seconds()` method is exclusive to `datetime.timedelta` objects.","error":"AttributeError: 'Duration' object has no attribute 'total_seconds'"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":"20.11.0","install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":3.8,"disk_size":"22.2M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.11,"mem_mb":3.8,"disk_size":"22.2M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.8,"import_time_s":0.07,"mem_mb":3.8,"disk_size":"23M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.07,"mem_mb":3.8,"disk_size":"23M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.16,"mem_mb":4.5,"disk_size":"24.2M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.18,"mem_mb":4.5,"disk_size":"24.2M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2,"import_time_s":0.14,"mem_mb":4.5,"disk_size":"25M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.13,"mem_mb":4.5,"disk_size":"25M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.2,"disk_size":"16.1M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.16,"mem_mb":4.2,"disk_size":"16.1M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.8,"import_time_s":0.14,"mem_mb":4.2,"disk_size":"17M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.2,"disk_size":"17M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.7,"disk_size":"15.8M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.14,"mem_mb":4.7,"disk_size":"15.7M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.8,"import_time_s":0.14,"mem_mb":4.7,"disk_size":"16M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.15,"mem_mb":4.7,"disk_size":"16M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.09,"mem_mb":3.8,"disk_size":"21.7M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.1,"mem_mb":3.8,"disk_size":"21.7M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.1,"import_time_s":0.09,"mem_mb":3.8,"disk_size":"22M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.08,"mem_mb":3.8,"disk_size":"22M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}