190 lines
5.2 KiB
Python
190 lines
5.2 KiB
Python
|
#
|
||
|
# This file is part of pyasn1 software.
|
||
|
#
|
||
|
# Copyright (c) 2005-2020, Ilya Etingof <etingof@gmail.com>
|
||
|
# License: https://pyasn1.readthedocs.io/en/latest/license.html
|
||
|
#
|
||
|
import datetime
|
||
|
|
||
|
from pyasn1 import error
|
||
|
from pyasn1.type import char
|
||
|
from pyasn1.type import tag
|
||
|
from pyasn1.type import univ
|
||
|
|
||
|
__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime']
|
||
|
|
||
|
NoValue = univ.NoValue
|
||
|
noValue = univ.noValue
|
||
|
|
||
|
|
||
|
class ObjectDescriptor(char.GraphicString):
|
||
|
__doc__ = char.GraphicString.__doc__
|
||
|
|
||
|
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
|
||
|
tagSet = char.GraphicString.tagSet.tagImplicitly(
|
||
|
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7)
|
||
|
)
|
||
|
|
||
|
# Optimization for faster codec lookup
|
||
|
typeId = char.GraphicString.getTypeId()
|
||
|
|
||
|
|
||
|
class TimeMixIn(object):
|
||
|
|
||
|
_yearsDigits = 4
|
||
|
_hasSubsecond = False
|
||
|
_optionalMinutes = False
|
||
|
_shortTZ = False
|
||
|
|
||
|
class FixedOffset(datetime.tzinfo):
|
||
|
"""Fixed offset in minutes east from UTC."""
|
||
|
|
||
|
# defaulted arguments required
|
||
|
# https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html
|
||
|
def __init__(self, offset=0, name='UTC'):
|
||
|
self.__offset = datetime.timedelta(minutes=offset)
|
||
|
self.__name = name
|
||
|
|
||
|
def utcoffset(self, dt):
|
||
|
return self.__offset
|
||
|
|
||
|
def tzname(self, dt):
|
||
|
return self.__name
|
||
|
|
||
|
def dst(self, dt):
|
||
|
return datetime.timedelta(0)
|
||
|
|
||
|
UTC = FixedOffset()
|
||
|
|
||
|
@property
|
||
|
def asDateTime(self):
|
||
|
"""Create :py:class:`datetime.datetime` object from a |ASN.1| object.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
:
|
||
|
new instance of :py:class:`datetime.datetime` object
|
||
|
"""
|
||
|
text = str(self)
|
||
|
if text.endswith('Z'):
|
||
|
tzinfo = TimeMixIn.UTC
|
||
|
text = text[:-1]
|
||
|
|
||
|
elif '-' in text or '+' in text:
|
||
|
if '+' in text:
|
||
|
text, plusminus, tz = text.partition('+')
|
||
|
else:
|
||
|
text, plusminus, tz = text.partition('-')
|
||
|
|
||
|
if self._shortTZ and len(tz) == 2:
|
||
|
tz += '00'
|
||
|
|
||
|
if len(tz) != 4:
|
||
|
raise error.PyAsn1Error('malformed time zone offset %s' % tz)
|
||
|
|
||
|
try:
|
||
|
minutes = int(tz[:2]) * 60 + int(tz[2:])
|
||
|
if plusminus == '-':
|
||
|
minutes *= -1
|
||
|
|
||
|
except ValueError:
|
||
|
raise error.PyAsn1Error('unknown time specification %s' % self)
|
||
|
|
||
|
tzinfo = TimeMixIn.FixedOffset(minutes, '?')
|
||
|
|
||
|
else:
|
||
|
tzinfo = None
|
||
|
|
||
|
if '.' in text or ',' in text:
|
||
|
if '.' in text:
|
||
|
text, _, ms = text.partition('.')
|
||
|
else:
|
||
|
text, _, ms = text.partition(',')
|
||
|
|
||
|
try:
|
||
|
ms = int(ms) * 1000
|
||
|
|
||
|
except ValueError:
|
||
|
raise error.PyAsn1Error('bad sub-second time specification %s' % self)
|
||
|
|
||
|
else:
|
||
|
ms = 0
|
||
|
|
||
|
if self._optionalMinutes and len(text) - self._yearsDigits == 6:
|
||
|
text += '0000'
|
||
|
elif len(text) - self._yearsDigits == 8:
|
||
|
text += '00'
|
||
|
|
||
|
try:
|
||
|
dt = datetime.datetime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
|
||
|
|
||
|
except ValueError:
|
||
|
raise error.PyAsn1Error('malformed datetime format %s' % self)
|
||
|
|
||
|
return dt.replace(microsecond=ms, tzinfo=tzinfo)
|
||
|
|
||
|
@classmethod
|
||
|
def fromDateTime(cls, dt):
|
||
|
"""Create |ASN.1| object from a :py:class:`datetime.datetime` object.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
dt: :py:class:`datetime.datetime` object
|
||
|
The `datetime.datetime` object to initialize the |ASN.1| object
|
||
|
from
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
:
|
||
|
new instance of |ASN.1| value
|
||
|
"""
|
||
|
text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S')
|
||
|
if cls._hasSubsecond:
|
||
|
text += '.%d' % (dt.microsecond // 1000)
|
||
|
|
||
|
if dt.utcoffset():
|
||
|
seconds = dt.utcoffset().seconds
|
||
|
if seconds < 0:
|
||
|
text += '-'
|
||
|
else:
|
||
|
text += '+'
|
||
|
text += '%.2d%.2d' % (seconds // 3600, seconds % 3600)
|
||
|
else:
|
||
|
text += 'Z'
|
||
|
|
||
|
return cls(text)
|
||
|
|
||
|
|
||
|
class GeneralizedTime(char.VisibleString, TimeMixIn):
|
||
|
__doc__ = char.VisibleString.__doc__
|
||
|
|
||
|
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
|
||
|
tagSet = char.VisibleString.tagSet.tagImplicitly(
|
||
|
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24)
|
||
|
)
|
||
|
|
||
|
# Optimization for faster codec lookup
|
||
|
typeId = char.VideotexString.getTypeId()
|
||
|
|
||
|
_yearsDigits = 4
|
||
|
_hasSubsecond = True
|
||
|
_optionalMinutes = True
|
||
|
_shortTZ = True
|
||
|
|
||
|
|
||
|
class UTCTime(char.VisibleString, TimeMixIn):
|
||
|
__doc__ = char.VisibleString.__doc__
|
||
|
|
||
|
#: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects
|
||
|
tagSet = char.VisibleString.tagSet.tagImplicitly(
|
||
|
tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23)
|
||
|
)
|
||
|
|
||
|
# Optimization for faster codec lookup
|
||
|
typeId = char.VideotexString.getTypeId()
|
||
|
|
||
|
_yearsDigits = 2
|
||
|
_hasSubsecond = False
|
||
|
_optionalMinutes = False
|
||
|
_shortTZ = False
|