diff --git a/evernote_backup/note_formatter_util.py b/evernote_backup/note_formatter_util.py index f6e81db..12b1097 100644 --- a/evernote_backup/note_formatter_util.py +++ b/evernote_backup/note_formatter_util.py @@ -1,6 +1,6 @@ import base64 import sys -from datetime import datetime +from datetime import datetime, timedelta from typing import Optional @@ -10,12 +10,15 @@ def fmt_time(timestamp: Optional[int]) -> Optional[str]: timestamp //= 1000 - if timestamp < _get_max_timestamp(): - date = datetime.utcfromtimestamp(timestamp) - else: + # https://dev.evernote.com/doc/reference/Types.html#Typedef_Timestamp + if timestamp < 0: + date = _date_from_past(timestamp) + elif timestamp >= _get_max_timestamp(): date = _date_from_future(timestamp) + else: + date = datetime.utcfromtimestamp(timestamp) - return date.strftime("%Y%m%dT%H%M%SZ") + return date.strftime(f"{date.year:04}%m%dT%H%M%SZ") def fmt_binary(binary_data: bytes) -> str: @@ -80,13 +83,23 @@ def _date_from_future(timestamp: int) -> datetime: # noqa: WPS210 m = mp + (3 if mp < 10 else -9) y += m <= 2 - day_time = datetime.utcfromtimestamp(timestamp % 86400) + try: + day_time = datetime.utcfromtimestamp(timestamp % 86400) + + return datetime( + year=y, + month=m, + day=d, + hour=day_time.hour, + minute=day_time.minute, + second=day_time.second, + ) + except (OverflowError, ValueError, OSError): + return datetime.max - return datetime( - year=y, - month=m, - day=d, - hour=day_time.hour, - minute=day_time.minute, - second=day_time.second, - ) + +def _date_from_past(timestamp: int) -> datetime: + try: + return datetime.utcfromtimestamp(0) - timedelta(seconds=abs(timestamp)) + except (OverflowError, ValueError, OSError): + return datetime.min diff --git a/tests/test_note_formatter.py b/tests/test_note_formatter.py index 3dd6ac5..ff3e763 100644 --- a/tests/test_note_formatter.py +++ b/tests/test_note_formatter.py @@ -168,7 +168,8 @@ def test_note_from_future(mocker): formatter = NoteFormatter() # 9999-12-31 23:59:59 - end_of_times = 253402300799999 + end_of_times_bad = 999999999999999 + end_of_times = 243402300799999 # Emulate windows limit mock_timestamp = mocker.patch( @@ -178,11 +179,29 @@ def test_note_from_future(mocker): note_from_future = Note( title="test", - created=end_of_times, + created=end_of_times_bad, updated=end_of_times, ) formatted_note = formatter.format_note(note_from_future) assert "99991231T235959Z" in formatted_note - assert "99991231T235959Z" in formatted_note + assert "96830210T061319Z" in formatted_note + + +def test_note_from_past(mocker): + formatter = NoteFormatter() + + before_times_bad = -999999999999999 + before_times = -50000000000000 + + note_from_future = Note( + title="test", + created=before_times_bad, + updated=before_times, + ) + + formatted_note = formatter.format_note(note_from_future) + + assert "00010101T000000Z" in formatted_note + assert "03850725T070640Z" in formatted_note