Parse a datetime/int/float/string and return a datetime.datetime.

    This function supports time zone offsets. When the input contains one,
    the output uses a timezone with a fixed offset from UTC.

    Raise ValueError if the input is well formatted but not a valid datetime.
    Raise ValueError if the input isn't well formatted.

def parse_datetime(value: Union[datetime, StrIntFloat]) -> datetime:
    """
    Parse a datetime/int/float/string and return a datetime.datetime.

    This function supports time zone offsets. When the input contains one,
    the output uses a timezone with a fixed offset from UTC.

    Raise ValueError if the input is well formatted but not a valid datetime.
    Raise ValueError if the input isn't well formatted.
    """
    if isinstance(value, datetime):
        return value

    number = get_numeric(value)
    if number is not None:
        return from_unix_seconds(number)

    match = datetime_re.match(cast(str, value))
    if not match:
        raise errors.DateTimeError()

    kw = match.groupdict()
    if kw['microsecond']:
        kw['microsecond'] = kw['microsecond'].ljust(6, '0')

    tzinfo_str = kw.pop('tzinfo')
    if tzinfo_str == 'Z':
        tzinfo = timezone.utc
    elif tzinfo_str is not None:
        offset_mins = int(tzinfo_str[-2:]) if len(tzinfo_str) > 3 else 0
        offset = 60 * int(tzinfo_str[1:3]) + offset_mins
        if tzinfo_str[0] == '-':
            offset = -offset
        tzinfo = timezone(timedelta(minutes=offset))
    else:
        tzinfo = None

    kw_: Dict[str, Union[int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
    kw_['tzinfo'] = tzinfo

    with change_exception(errors.DateTimeError, ValueError):
        return datetime(**kw_)