def parse_schedule(schedule: str,
                       action: str) -> Tuple[Union[int, None], Union[str, None], Union[datetime.datetime, None]]:
        """Parse the given *schedule* and validate it.

        :param schedule: a string representing a timestamp, e.g. ``2023-04-14T20:27:00+00:00``.
        :param action: the action to be scheduled (``restart``, ``switchover``, or ``failover``).

        :returns: a tuple composed of 3 items:

            * Suggested HTTP status code for a response:

                * ``None``: if no issue was faced while parsing, leaving it up to the caller to decide the status; or
                * ``400``: if no timezone information could be found in *schedule*; or
                * ``422``: if *schedule* is invalid -- in the past or not parsable.

            * An error message, if any error is faced, otherwise ``None``;
            * Parsed *schedule*, if able to parse, otherwise ``None``.

        """
        error = None
        scheduled_at = None
        try:
            scheduled_at = dateutil.parser.parse(schedule)
            if scheduled_at.tzinfo is None:
                error = 'Timezone information is mandatory for the scheduled {0}'.format(action)
                status_code = 400
            elif scheduled_at < datetime.datetime.now(tzutc):
                error = 'Cannot schedule {0} in the past'.format(action)
                status_code = 422
            else:
                status_code = None
        except (ValueError, TypeError):
            logger.exception('Invalid scheduled %s time: %s', action, schedule)
            error = 'Unable to parse scheduled timestamp. It should be in an unambiguous format, e.g. ISO 8601'
            status_code = 422
        return status_code, error, scheduled_at
Parse the given *schedule* and validate it.

        :param schedule: a string representing a timestamp, e.g. ``2023-04-14T20:27:00+00:00``.
        :param action: the action to be scheduled (``restart``, ``switchover``, or ``failover``).

        :returns: a tuple composed of 3 items:

            * Suggested HTTP status code for a response:

                * ``None``: if no issue was faced while parsing, leaving it up to the caller to decide the status; or
                * ``400``: if no timezone information could be found in *schedule*; or
                * ``422``: if *schedule* is invalid -- in the past or not parsable.

            * An error message, if any error is faced, otherwise ``None``;
            * Parsed *schedule*, if able to parse, otherwise ``None``.

        
patroni/api.py