1#!/usr/bin/env python 2# 3# This file is part of pyasn1 software. 4# 5# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com> 6# License: http://snmplabs.com/pyasn1/license.html 7# 8import datetime 9 10from pyasn1 import error 11from pyasn1.compat import dateandtime 12from pyasn1.compat import string 13from pyasn1.type import char 14from pyasn1.type import tag 15from pyasn1.type import univ 16 17__all__ = ['ObjectDescriptor', 'GeneralizedTime', 'UTCTime'] 18 19NoValue = univ.NoValue 20noValue = univ.noValue 21 22 23class ObjectDescriptor(char.GraphicString): 24 __doc__ = char.GraphicString.__doc__ 25 26 #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects 27 tagSet = char.GraphicString.tagSet.tagImplicitly( 28 tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 7) 29 ) 30 31 # Optimization for faster codec lookup 32 typeId = char.GraphicString.getTypeId() 33 34 35class TimeMixIn(object): 36 37 _yearsDigits = 4 38 _hasSubsecond = False 39 _optionalMinutes = False 40 _shortTZ = False 41 42 class FixedOffset(datetime.tzinfo): 43 """Fixed offset in minutes east from UTC.""" 44 45 # defaulted arguments required 46 # https: // docs.python.org / 2.3 / lib / datetime - tzinfo.html 47 def __init__(self, offset=0, name='UTC'): 48 self.__offset = datetime.timedelta(minutes=offset) 49 self.__name = name 50 51 def utcoffset(self, dt): 52 return self.__offset 53 54 def tzname(self, dt): 55 return self.__name 56 57 def dst(self, dt): 58 return datetime.timedelta(0) 59 60 UTC = FixedOffset() 61 62 @property 63 def asDateTime(self): 64 """Create :py:class:`datetime.datetime` object from a |ASN.1| object. 65 66 Returns 67 ------- 68 : 69 new instance of :py:class:`datetime.datetime` object 70 """ 71 text = str(self) 72 if text.endswith('Z'): 73 tzinfo = TimeMixIn.UTC 74 text = text[:-1] 75 76 elif '-' in text or '+' in text: 77 if '+' in text: 78 text, plusminus, tz = string.partition(text, '+') 79 else: 80 text, plusminus, tz = string.partition(text, '-') 81 82 if self._shortTZ and len(tz) == 2: 83 tz += '00' 84 85 if len(tz) != 4: 86 raise error.PyAsn1Error('malformed time zone offset %s' % tz) 87 88 try: 89 minutes = int(tz[:2]) * 60 + int(tz[2:]) 90 if plusminus == '-': 91 minutes *= -1 92 93 except ValueError: 94 raise error.PyAsn1Error('unknown time specification %s' % self) 95 96 tzinfo = TimeMixIn.FixedOffset(minutes, '?') 97 98 else: 99 tzinfo = None 100 101 if '.' in text or ',' in text: 102 if '.' in text: 103 text, _, ms = string.partition(text, '.') 104 else: 105 text, _, ms = string.partition(text, ',') 106 107 try: 108 ms = int(ms) * 10000 109 110 except ValueError: 111 raise error.PyAsn1Error('bad sub-second time specification %s' % self) 112 113 else: 114 ms = 0 115 116 if self._optionalMinutes and len(text) - self._yearsDigits == 6: 117 text += '0000' 118 elif len(text) - self._yearsDigits == 8: 119 text += '00' 120 121 try: 122 dt = dateandtime.strptime(text, self._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S') 123 124 except ValueError: 125 raise error.PyAsn1Error('malformed datetime format %s' % self) 126 127 return dt.replace(microsecond=ms, tzinfo=tzinfo) 128 129 @classmethod 130 def fromDateTime(cls, dt): 131 """Create |ASN.1| object from a :py:class:`datetime.datetime` object. 132 133 Parameters 134 ---------- 135 dt: :py:class:`datetime.datetime` object 136 The `datetime.datetime` object to initialize the |ASN.1| object 137 from 138 139 Returns 140 ------- 141 : 142 new instance of |ASN.1| value 143 """ 144 text = dt.strftime(cls._yearsDigits == 4 and '%Y%m%d%H%M%S' or '%y%m%d%H%M%S') 145 if cls._hasSubsecond: 146 text += '.%d' % (dt.microsecond // 10000) 147 148 if dt.utcoffset(): 149 seconds = dt.utcoffset().seconds 150 if seconds < 0: 151 text += '-' 152 else: 153 text += '+' 154 text += '%.2d%.2d' % (seconds // 3600, seconds % 3600) 155 else: 156 text += 'Z' 157 158 return cls(text) 159 160 161class GeneralizedTime(char.VisibleString, TimeMixIn): 162 __doc__ = char.VisibleString.__doc__ 163 164 #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects 165 tagSet = char.VisibleString.tagSet.tagImplicitly( 166 tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 24) 167 ) 168 169 # Optimization for faster codec lookup 170 typeId = char.VideotexString.getTypeId() 171 172 _yearsDigits = 4 173 _hasSubsecond = True 174 _optionalMinutes = True 175 _shortTZ = True 176 177 178class UTCTime(char.VisibleString, TimeMixIn): 179 __doc__ = char.VisibleString.__doc__ 180 181 #: Default :py:class:`~pyasn1.type.tag.TagSet` object for |ASN.1| objects 182 tagSet = char.VisibleString.tagSet.tagImplicitly( 183 tag.Tag(tag.tagClassUniversal, tag.tagFormatSimple, 23) 184 ) 185 186 # Optimization for faster codec lookup 187 typeId = char.VideotexString.getTypeId() 188 189 _yearsDigits = 2 190 _hasSubsecond = False 191 _optionalMinutes = False 192 _shortTZ = False 193