Add datetime converter and util #54

Open
Daggy1234 wants to merge 3 commits from Daggy1234/patch-1 into 2.0
3 changed files with 52 additions and 0 deletions

View File

@ -26,6 +26,7 @@ from __future__ import annotations
import re
import inspect
import datetime
from typing import (
Any,
Dict,
@ -823,7 +824,23 @@ class PartialEmojiConverter(Converter[discord.PartialEmoji]):
raise PartialEmojiConversionFailure(argument)
class FormattedDatetimeConverter(Converter[Tuple[datetime.datetime, Optional[str]]]):
"""Converts a discord style datetime to a :class:`datetime.datetime`.
Also returns the flag used to format the timestamp if present.
This is done by extracting the epoch from the string.
.. versionadd:: 2.0
Raise :exc:`.FormattedDatetimeConversionFailure` instead of generic :exc:`.BadArgument`
"""
async def convert(self, ctx: Context, argument: str) -> Tuple[datetime.datetime, Optional[str]]:
match = discord.utils.resolve_formatted_dt(argument)
if match:
return match
raise FormattedDatetimeConversionFailure(argument)
class GuildStickerConverter(IDConverter[discord.GuildSticker]):
"""Converts to a :class:`~discord.GuildSticker`.

View File

@ -98,6 +98,7 @@ __all__ = (
'MissingFlagArgument',
'TooManyFlags',
'MissingRequiredFlag',
'FormattedDatetimeConversionFailure',
)
class CommandError(DiscordException):
@ -995,3 +996,16 @@ class MissingFlagArgument(FlagError):
def __init__(self, flag: Flag) -> None:
self.flag: Flag = flag
super().__init__(f'Flag {flag.name!r} does not have an argument')
class FormattedDatetimeConversionFailure(BadArgument):
"""Exception raised when the timestamp provided does not match the correct
format.
This inherits from :exc:`BadArgument`
Attributes
-----------
argument: :class:`str`
The emoji supplied by the caller that did not match the regex
"""
def __init__(self, argument):
self.argument = argument
super().__init__(f'Timestamp "{argument}" wasn\'t formatted correctly.')

View File

@ -1021,3 +1021,24 @@ def raise_expected_coro(coro, error: str)-> TypeError:
if not asyncio.iscoroutinefunction(coro):
raise TypeError(error)
return coro
def resolve_formatted_dt(formatted_dt: str) -> Optional[Tuple[datetime.datetime, Optional[str]]]:
"""A helper function that can take discord formatted datetimes like those produced by :meth:`~discord.utils.format_dt`
and return the relevant datetime.
If unable to parse a datetime from formatted_dt, it will return None.
.. versionadded:: 2.0
Parameters
-----------
formatted_dt: :class:`str`
The formatted datetime string.
Returns
--------
Optional[Tuple[:class:`datetime.datetime`, Optional[:class:`str`]]
A tuple containing the datetime and the flag if present. Otherwise None.
"""
match = re.match(r'<t:(-?[0-9]+)(:[tTdDfFR])?>$', formatted_dt)
if match:
try:
return datetime.datetime.fromtimestamp(int(match.group(1)), tz=datetime.timezone.utc), match.group(2)
except (ValueError, OverflowError, OSError):
return