### Navigation

- index
- modules |
- vSphere Automation SDK for Python 6.5.0 documentation »
- Module code »

# Source code for vmware.vapi.bindings.datetime_helper

    """
    Utility library for converting to/from datetime objects in Python Bindings
    """
    __author__ = 'VMware, Inc'
    __copyright__ = 'Copyright (c) 2015 VMware, Inc.  All rights reserved.'

    import logging
    import re
    from datetime import datetime, tzinfo, timedelta

    from vmware.vapi.l10n.runtime import message_factory
    from vmware.vapi.exception import CoreException

    logger = logging.getLogger(__name__)

    class DateTimeConverter(object):
        """
        Helper class to convert to/from Python datetime strings to
        datetime objects.

        Datetime is represented as :class:`vmware.vapi.data.value.StringValue` in the
        vAPI Runtime. Datetime is a primitive string that follows a subset of
        ISO 8601. DateTime string represents a complete date plus hours, minutes,
        seconds and a decimal fraction of a second:

            YYYY-MM-DDThh:mm:ss.sssZ (e.g. 1878-03-03T19:20:30.000Z)
            where:
                YYYY = four-digit year (years BC are not supported;
                                        0001 = 1 AD is the first valid year,
                                        0000 = 1 BC is not allowed)
                MM = two-digit month (01=January, ..., 12=December)
                DD = two-digit day of month (01 through 31)
                "T" = separator; appears literally in the string
                hh = two digits of hour (00 through 23; 24 NOT allowed;
                                         am/pm NOT allowed)
                mm = two digits of minute (00 through 59)
                ss = two digits of second (00 through 59)
                sss = exactly three digits representing milliseconds
                "Z" = UTC time zone designator; appears literally in the string
        """
        _dt_pattern = \
            '(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2}).(\\d{3})Z'
        _dt_expr = re.compile(_dt_pattern)

        @staticmethod
        def convert_to_datetime(datetime_str):
            """
            Parse ISO 8601 date time from string.

            :type  datetime_str: :class:`str`
            :param datetime_str: Datetime in string representation that is in
                                 YYYY-MM-DDThh:mm:ss.sssZ format
            :rtype: :class:`datetime.datetime`
            :return: Datetime object
            """
            datetime_val = None
            match = DateTimeConverter._dt_expr.match(datetime_str)
            if not match or len(datetime_str) > 24:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.invalid.format',
                    datetime_str, DateTimeConverter._dt_pattern)
                logger.debug(msg)
                raise CoreException(msg)

            year = int(match.group(1))
            month = int(match.group(2))
            if month < 1 or month > 12:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.month.invalid',
                     datetime_str)
                logger.debug(msg)
                raise CoreException(msg)

            day = int(match.group(3))
            if day < 1 or day > 31:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.day.invalid',
                     datetime_str)
                logger.debug(msg)
                raise CoreException(msg)

            hour = int(match.group(4))
            if hour > 23:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.hour.invalid',
                     datetime_str)
                logger.debug(msg)
                raise CoreException(msg)

            minute = int(match.group(5))
            if minute > 59:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.minute.invalid',
                     datetime_str)
                logger.debug(msg)
                raise CoreException(msg)

            second = int(match.group(6))
            if second > 59:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.second.invalid',
                     datetime_str)
                logger.debug(msg)
                raise CoreException(msg)

            # Convert from millisecond to microsecond precision
            microsecond = int('%s000' % match.group(7))
            try:
                datetime_val = datetime(year=year, month=month, day=day, hour=hour,
                                        minute=minute, second=second,
                                        microsecond=microsecond)
            except Exception as err:
                msg = message_factory.get_message(
                    'vapi.bindings.typeconverter.datetime.deserialize.invalid.time',
                    datetime_str, str(err))
                logger.error(msg)
                raise CoreException(msg)

            return datetime_val

        @staticmethod
        def convert_from_datetime(datetime_obj):
            """
            Convert from Python native datetime object to the datetime format in
            vAPI Runtime i.e. YYYY-MM-DDThh:mm:ss.sssZ.

            datetime objects returned by datetime.now() or datetime.utcnow() does
            not contain any timezone information. The caller to this method should
            only pass datetime objects that have time in UTC timezone.

            datetime objects have microsecond precision but the vAPI datetime
            string format has millisecond precision. The method will truncate the
            microsecond to millisecond and won't do any rounding of the value.

            :type  datetime_obj: :class:`datetime.datetime`
            :param datetime_obj: Datetime object with UTC time
            :rtype: :class:`str`
            :return: String representation of the input datetime object
            """
            if datetime_obj.tzinfo:
                # If tzinfo object is present, it should be in UTC timezone
                # i.e. timedelta is 0
                if datetime_obj.tzinfo.utcoffset(datetime_obj) != timedelta(0):
                    msg = message_factory.get_message(
                        'vapi.bindings.typeconverter.datetime.serialize.invalid.tz',
                        str(datetime_obj))
                    logger.error(msg)
                    raise CoreException(msg)

            # Since it is UTC timezone, replacing it with None
            # the output of isoformat() does not match vAPI runtime datetime string
            # format if tzinfo is present in the datetime object
            datetime_obj = datetime_obj.replace(tzinfo=None)

            iso_str = datetime_obj.isoformat()
            if datetime_obj.microsecond:
                # datetime prints microseconds, reducing precision to milliseconds
                iso_str = iso_str[:-3]
            else:
                # Python .isoformat does not print microsecond if it is 0
                iso_str = '%s.000' % iso_str
            return '%sZ' % iso_str  # Adding Z to indicate it is UTC timezone

    class UTC(tzinfo):
        """
        tzinfo class for UTC timezone
        """
        # pylint: disable=W0613
        def utcoffset(self, dt):
            return timedelta(0)

        def tzname(self, dt):
            return 'UTC'

        def dst(self, dt):
            return timedelta(0)

    def convert_to_utc(dt):
        """
        Convert a given datetime object to UTC timezone
        """
        if dt:
            return dt.astimezone(UTC())

### Navigation

- index
- modules |
- vSphere Automation SDK for Python 6.5.0 documentation »
- Module code »