Fix (de)serialization of negative timestamps (#1241)

This commit is contained in:
binares 2019-08-01 19:47:38 +03:00 committed by Lonami
parent 2ace4fde41
commit 2b277dd558
2 changed files with 27 additions and 12 deletions

View File

@ -2,14 +2,18 @@
This module contains the BinaryReader utility class. This module contains the BinaryReader utility class.
""" """
import os import os
from datetime import datetime, timezone from datetime import datetime, timezone, timedelta
from io import BufferedReader, BytesIO from io import BufferedReader, BytesIO
from struct import unpack from struct import unpack
import time
from ..errors import TypeNotFoundError from ..errors import TypeNotFoundError
from ..tl.alltlobjects import tlobjects from ..tl.alltlobjects import tlobjects
from ..tl.core import core_objects from ..tl.core import core_objects
_EPOCH_NAIVE = datetime(*time.gmtime(0)[:6])
_EPOCH = _EPOCH_NAIVE.replace(tzinfo=timezone.utc)
class BinaryReader: class BinaryReader:
""" """
@ -120,10 +124,7 @@ class BinaryReader:
into a Python datetime object. into a Python datetime object.
""" """
value = self.read_int() value = self.read_int()
if value == 0: return _EPOCH + timedelta(seconds=value)
return None
else:
return datetime.fromtimestamp(value, tz=timezone.utc)
def tgread_object(self): def tgread_object(self):
"""Reads a Telegram object.""" """Reads a Telegram object."""

View File

@ -1,7 +1,21 @@
import base64 import base64
import json import json
import struct import struct
from datetime import datetime, date, timedelta from datetime import datetime, date, timedelta, timezone
import time
_EPOCH_NAIVE = datetime(*time.gmtime(0)[:6])
_EPOCH_NAIVE_LOCAL = datetime(*time.localtime(0)[:6])
_EPOCH = _EPOCH_NAIVE.replace(tzinfo=timezone.utc)
def _datetime_to_timestamp(dt):
# If no timezone is specified, it is assumed to be in utc zone
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
# We use .total_seconds() method instead of simply dt.timestamp(),
# because on Windows the latter raises OSError on datetimes ~< datetime(1970,1,1)
return int((dt - _EPOCH).total_seconds())
def _json_default(value): def _json_default(value):
@ -121,21 +135,21 @@ class TLObject:
@staticmethod @staticmethod
def serialize_datetime(dt): def serialize_datetime(dt):
if not dt: if not dt and not isinstance(dt, timedelta):
return b'\0\0\0\0' return b'\0\0\0\0'
if isinstance(dt, datetime): if isinstance(dt, datetime):
dt = int(dt.timestamp()) dt = _datetime_to_timestamp(dt)
elif isinstance(dt, date): elif isinstance(dt, date):
dt = int(datetime(dt.year, dt.month, dt.day).timestamp()) dt = _datetime_to_timestamp(datetime(dt.year, dt.month, dt.day))
elif isinstance(dt, float): elif isinstance(dt, float):
dt = int(dt) dt = int(dt)
elif isinstance(dt, timedelta): elif isinstance(dt, timedelta):
# Timezones are tricky. datetime.now() + ... timestamp() works # Timezones are tricky. datetime.utcnow() + ... timestamp() works
dt = int((datetime.now() + dt).timestamp()) dt = _datetime_to_timestamp(datetime.utcnow() + dt)
if isinstance(dt, int): if isinstance(dt, int):
return struct.pack('<I', dt) return struct.pack('<i', dt)
raise TypeError('Cannot interpret "{}" as a date.'.format(dt)) raise TypeError('Cannot interpret "{}" as a date.'.format(dt))