From 244440d6dfec8873159c77d741c5b01cecc2cc20 Mon Sep 17 00:00:00 2001 From: Favo Yang Date: Wed, 18 Oct 2023 17:46:12 +0800 Subject: [PATCH] fix: add support for negative timestamps (close #38) (#48) * fix: negative timestamp in note_formatter_util (close #38) * fix: convert negative timestamp * fix: zeropad year for linux strftime --------- Co-authored-by: vzhd1701 --- evernote_backup/note_formatter_util.py | 41 +++++++++++++++++--------- tests/test_note_formatter.py | 25 ++++++++++++++-- 2 files changed, 49 insertions(+), 17 deletions(-) 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