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__)


[docs]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
[docs] 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
[docs] 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
[docs]class UTC(tzinfo): """ tzinfo class for UTC timezone """ # pylint: disable=W0613
[docs] def utcoffset(self, dt): return timedelta(0)
[docs] def tzname(self, dt): return 'UTC'
[docs] def dst(self, dt): return timedelta(0)
[docs]def convert_to_utc(dt): """ Convert a given datetime object to UTC timezone """ if dt: return dt.astimezone(UTC())