chg: Normalize to_datetime conversion

pull/531/head
Raphaël Vinot 2020-01-27 20:14:14 +01:00
parent 32445973bd
commit f43266fcf2
1 changed files with 74 additions and 42 deletions

View File

@ -4,6 +4,7 @@ from datetime import timezone, datetime, date
import json import json
import os import os
import base64 import base64
import sys
from io import BytesIO, IOBase from io import BytesIO, IOBase
from zipfile import ZipFile from zipfile import ZipFile
import uuid import uuid
@ -51,6 +52,42 @@ def _int_to_str(d: dict):
d[k] = str(v) d[k] = str(v)
return d return d
def _make_datetime(value) -> datetime:
if isinstance(value, (int, float)):
# Timestamp
value = datetime.fromtimestamp(value)
elif isinstance(value, str):
if sys.version_info >= (3, 7):
try:
# faster
value = datetime.fromisoformat(value)
except Exception:
value = parse(value)
else:
try:
# faster
if '+' in value or '-' in value:
value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f%z")
elif '.' in value:
value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
elif 'T' in value:
value = datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
else:
value = datetime.strptime(value, "%Y-%m-%d")
except Exception:
value = parse(value)
elif isinstance(value, date):
value = datetime.combine(value, datetime.min.time())
elif isinstance(value, datetime):
pass
else:
raise PyMISPError(f'Invalid format for {value}: {type(value)}.')
if not value.tzinfo:
# set localtimezone if not present
value = value.astimezone()
return value
def make_bool(value: Union[bool, int, str, dict, list, None]) -> bool: def make_bool(value: Union[bool, int, str, dict, list, None]) -> bool:
if isinstance(value, bool): if isinstance(value, bool):
@ -179,21 +216,7 @@ class MISPAttribute(AbstractMISP):
def __setattr__(self, name, value): def __setattr__(self, name, value):
if name in ['first_seen', 'last_seen']: if name in ['first_seen', 'last_seen']:
if isinstance(value, (int, float)): value = _make_datetime(value)
# Timestamp
value = datetime.fromtimestamp(value)
elif isinstance(value, str):
value = parse(value)
elif isinstance(value, date):
value = datetime.combine(falue, datetime.min.time())
elif isinstance(value, datetime):
pass
else:
raise PyMISPError(f'Invalid format for {name}: {type(value)}.')
if not value.tzinfo:
# set localtimezone if not present
value = value.astimezone()
if name == 'last_seen' and hasattr(self, 'first_seen') and self.first_seen > value: if name == 'last_seen' and hasattr(self, 'first_seen') and self.first_seen > value:
raise PyMISPError('last_seen ({value}) has to be after first_seen ({self.first_seen})') raise PyMISPError('last_seen ({value}) has to be after first_seen ({self.first_seen})')
@ -334,12 +357,16 @@ class MISPAttribute(AbstractMISP):
raise NewAttributeError('The value of the attribute is required.') raise NewAttributeError('The value of the attribute is required.')
if self.type == 'datetime' and isinstance(self.value, str): if self.type == 'datetime' and isinstance(self.value, str):
try: try:
if '+' in self.value or '-' in self.value: # Faster
self.value = datetime.strptime(self.value, "%Y-%m-%dT%H:%M:%S.%f%z") if sys.version_info >= (3, 7):
elif '.' in self.value: self.value = datetime.fromisoformat(self.value)
self.value = datetime.strptime(self.value, "%Y-%m-%dT%H:%M:%S.%f")
else: else:
self.value = datetime.strptime(self.value, "%Y-%m-%dT%H:%M:%S") if '+' in self.value or '-' in self.value:
self.value = datetime.strptime(self.value, "%Y-%m-%dT%H:%M:%S.%f%z")
elif '.' in self.value:
self.value = datetime.strptime(self.value, "%Y-%m-%dT%H:%M:%S.%f")
else:
self.value = datetime.strptime(self.value, "%Y-%m-%dT%H:%M:%S")
except ValueError: except ValueError:
# Slower, but if the other ones fail, that's a good fallback # Slower, but if the other ones fail, that's a good fallback
self.value = parse(self.value) self.value = parse(self.value)
@ -384,7 +411,10 @@ class MISPAttribute(AbstractMISP):
fs = kwargs.pop('first_seen') fs = kwargs.pop('first_seen')
try: try:
# Faster # Faster
self.first_seen = datetime.strptime(fs, "%Y-%m-%dT%H:%M:%S.%f%z") if sys.version_info >= (3, 7):
self.first_seen = datetime.fromisoformat(fs)
else:
self.first_seen = datetime.strptime(fs, "%Y-%m-%dT%H:%M:%S.%f%z")
except: except:
# Use __setattr__ # Use __setattr__
self.first_seen = fs self.first_seen = fs
@ -393,7 +423,10 @@ class MISPAttribute(AbstractMISP):
ls = kwargs.pop('last_seen') ls = kwargs.pop('last_seen')
try: try:
# Faster # Faster
self.last_seen = datetime.strptime(kwargs.pop('last_seen'), "%Y-%m-%dT%H:%M:%S.%f%z") if sys.version_info >= (3, 7):
self.last_seen = datetime.fromisoformat(ls)
else:
self.last_seen = datetime.strptime(ls, "%Y-%m-%dT%H:%M:%S.%f%z")
except: except:
# Use __setattr__ # Use __setattr__
self.last_seen = ls self.last_seen = ls
@ -647,21 +680,7 @@ class MISPObject(AbstractMISP):
def __setattr__(self, name, value): def __setattr__(self, name, value):
if name in ['first_seen', 'last_seen']: if name in ['first_seen', 'last_seen']:
if isinstance(value, datetime): value = _make_datetime(value)
pass
elif isinstance(value, (int, float)):
# Timestamp
value = datetime.fromtimestamp(value)
elif isinstance(value, str):
value = parse(value)
elif isinstance(value, date):
value = datetime.combine(falue, datetime.min.time())
else:
raise PyMISPError(f'Invalid format for {name}: {type(value)}.')
if not value.tzinfo:
# set localtimezone if not present
value = value.astimezone()
if name == 'last_seen' and hasattr(self, 'first_seen') and self.first_seen > value: if name == 'last_seen' and hasattr(self, 'first_seen') and self.first_seen > value:
raise PyMISPError('last_seen ({value}) has to be after first_seen ({self.first_seen})') raise PyMISPError('last_seen ({value}) has to be after first_seen ({self.first_seen})')
@ -750,8 +769,11 @@ class MISPObject(AbstractMISP):
fs = kwargs.pop('first_seen') fs = kwargs.pop('first_seen')
try: try:
# Faster # Faster
self.first_seen = datetime.strptime(fs, "%Y-%m-%dT%H:%M:%S.%f%z") if sys.version_info >= (3, 7):
except: self.first_seen = datetime.fromisoformat(fs)
else:
self.first_seen = datetime.strptime(fs, "%Y-%m-%dT%H:%M:%S.%f%z")
except Exception:
# Use __setattr__ # Use __setattr__
self.first_seen = fs self.first_seen = fs
@ -759,8 +781,11 @@ class MISPObject(AbstractMISP):
ls = kwargs.pop('last_seen') ls = kwargs.pop('last_seen')
try: try:
# Faster # Faster
self.last_seen = datetime.strptime(kwargs.pop('last_seen'), "%Y-%m-%dT%H:%M:%S.%f%z") if sys.version_info >= (3, 7):
except: self.last_seen = datetime.fromisoformat(ls)
else:
self.last_seen = datetime.strptime(ls, "%Y-%m-%dT%H:%M:%S.%f%z")
except Exception:
# Use __setattr__ # Use __setattr__
self.last_seen = ls self.last_seen = ls
@ -1113,7 +1138,14 @@ class MISPEvent(AbstractMISP):
if isinstance(value, date): if isinstance(value, date):
pass pass
elif isinstance(value, str): elif isinstance(value, str):
value = parse(value).date() if sys.version_info >= (3, 7):
try:
# faster
value = date.fromisoformat(fs)
except Exception:
value = parse(value).date()
else:
value = parse(value).date()
elif isinstance(value, (int, float)): elif isinstance(value, (int, float)):
value = date.fromtimestamp(value) value = date.fromtimestamp(value)
elif isinstance(value, datetime): elif isinstance(value, datetime):