Skip to content
45 changes: 45 additions & 0 deletions pydis_core/utils/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import dateutil.parser
from datetime import UTC, datetime
from discord.ext.commands import BadArgument, Context, Converter

Check failure on line 3 in pydis_core/utils/converter.py

View workflow job for this annotation

GitHub Actions / lint-test / Run Linting & Test Suites (3.11)

Ruff (I001)

pydis_core/utils/converter.py:1:1: I001 Import block is un-sorted or un-formatted

Check failure on line 3 in pydis_core/utils/converter.py

View workflow job for this annotation

GitHub Actions / lint-test / Run Linting & Test Suites (3.12)

Ruff (I001)

pydis_core/utils/converter.py:1:1: I001 Import block is un-sorted or un-formatted

class ISODateTime(Converter):
"""Converts an ISO-8601 datetime string into a datetime.datetime."""

async def convert(self, ctx: Context, datetime_string: str) -> datetime:

Check failure on line 8 in pydis_core/utils/converter.py

View workflow job for this annotation

GitHub Actions / lint-test / Run Linting & Test Suites (3.11)

Ruff (ARG002)

pydis_core/utils/converter.py:8:29: ARG002 Unused method argument: `ctx`

Check failure on line 8 in pydis_core/utils/converter.py

View workflow job for this annotation

GitHub Actions / lint-test / Run Linting & Test Suites (3.12)

Ruff (ARG002)

pydis_core/utils/converter.py:8:29: ARG002 Unused method argument: `ctx`
"""
Converts a ISO-8601 `datetime_string` into a `datetime.datetime` object.

The converter is flexible in the formats it accepts, as it uses the `isoparse` method of
`dateutil.parser`. In general, it accepts datetime strings that start with a date,
optionally followed by a time. Specifying a timezone offset in the datetime string is
supported, but the `datetime` object will be converted to UTC. If no timezone is specified, the datetime will
be assumed to be in UTC already. In all cases, the returned object will have the UTC timezone.

See: https://dateutil.readthedocs.io/en/stable/parser.html#dateutil.parser.isoparse

Formats that are guaranteed to be valid by our tests are:

- `YYYY-mm-ddTHH:MM:SSZ` | `YYYY-mm-dd HH:MM:SSZ`
- `YYYY-mm-ddTHH:MM:SS±HH:MM` | `YYYY-mm-dd HH:MM:SS±HH:MM`
- `YYYY-mm-ddTHH:MM:SS±HHMM` | `YYYY-mm-dd HH:MM:SS±HHMM`
- `YYYY-mm-ddTHH:MM:SS±HH` | `YYYY-mm-dd HH:MM:SS±HH`
- `YYYY-mm-ddTHH:MM:SS` | `YYYY-mm-dd HH:MM:SS`
- `YYYY-mm-ddTHH:MM` | `YYYY-mm-dd HH:MM`
- `YYYY-mm-dd`
- `YYYY-mm`
- `YYYY`

Note: ISO-8601 specifies a `T` as the separator between the date and the time part of the
datetime string. The converter accepts both a `T` and a single space character.
"""
try:
dt = dateutil.parser.isoparse(datetime_string)
except ValueError:
raise BadArgument(f"`{datetime_string}` is not a valid ISO-8601 datetime string")

if dt.tzinfo:
dt = dt.astimezone(UTC)
else: # Without a timezone, assume it represents UTC.
dt = dt.replace(tzinfo=UTC)

return dt
Loading