diff --git a/swh/storage/postgresql/converters.py b/swh/storage/postgresql/converters.py --- a/swh/storage/postgresql/converters.py +++ b/swh/storage/postgresql/converters.py @@ -1,9 +1,10 @@ -# Copyright (C) 2015-2020 The Software Heritage developers +# Copyright (C) 2015-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import datetime +import math from typing import Any, Dict, Optional import warnings @@ -106,7 +107,9 @@ return TimestampWithTimezone( timestamp=Timestamp( - seconds=int(date.timestamp()), microseconds=date.microsecond, + # we use floor() instead of int() to round down, because of negative dates + seconds=math.floor(date.timestamp()), + microseconds=date.microsecond, ), offset=offset, negative_utc=neg_utc_offset, diff --git a/swh/storage/tests/test_postgresql_converters.py b/swh/storage/tests/test_postgresql_converters.py --- a/swh/storage/tests/test_postgresql_converters.py +++ b/swh/storage/tests/test_postgresql_converters.py @@ -20,57 +20,144 @@ from swh.storage.postgresql import converters -def test_date_to_db(): - date_to_db = converters.date_to_db - assert date_to_db(None) == {"timestamp": None, "offset": 0, "neg_utc_offset": None} - - assert date_to_db( - TimestampWithTimezone( - timestamp=Timestamp(seconds=1234567890, microseconds=0,), - offset=120, - negative_utc=False, - ) - ) == { - "timestamp": "2009-02-13T23:31:30+00:00", - "offset": 120, - "neg_utc_offset": False, - } - - assert date_to_db( - TimestampWithTimezone( - timestamp=Timestamp(seconds=1123456789, microseconds=0,), - offset=0, - negative_utc=True, - ) - ) == { - "timestamp": "2005-08-07T23:19:49+00:00", - "offset": 0, - "neg_utc_offset": True, - } - - assert date_to_db( - TimestampWithTimezone( - timestamp=Timestamp(seconds=1234567890, microseconds=0,), - offset=42, - negative_utc=False, - ) - ) == { - "timestamp": "2009-02-13T23:31:30+00:00", - "offset": 42, - "neg_utc_offset": False, - } - - assert date_to_db( - TimestampWithTimezone( - timestamp=Timestamp(seconds=1634366813, microseconds=0,), - offset=-120, - negative_utc=False, +@pytest.mark.parametrize( + "model_date,db_date", + [ + (None, {"timestamp": None, "offset": 0, "neg_utc_offset": None}), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=1234567890, microseconds=0,), + offset=120, + negative_utc=False, + ), + { + "timestamp": "2009-02-13T23:31:30+00:00", + "offset": 120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=1123456789, microseconds=0,), + offset=0, + negative_utc=True, + ), + { + "timestamp": "2005-08-07T23:19:49+00:00", + "offset": 0, + "neg_utc_offset": True, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=1234567890, microseconds=0,), + offset=42, + negative_utc=False, + ), + { + "timestamp": "2009-02-13T23:31:30+00:00", + "offset": 42, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=1634366813, microseconds=0,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "2021-10-16T06:46:53+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=0, microseconds=0,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "1970-01-01T00:00:00+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=0, microseconds=1,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "1970-01-01T00:00:00.000001+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=-1, microseconds=0,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "1969-12-31T23:59:59+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=-1, microseconds=1,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "1969-12-31T23:59:59.000001+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=-3600, microseconds=0,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "1969-12-31T23:00:00+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ( + TimestampWithTimezone( + timestamp=Timestamp(seconds=-3600, microseconds=1,), + offset=-120, + negative_utc=False, + ), + { + "timestamp": "1969-12-31T23:00:00.000001+00:00", + "offset": -120, + "neg_utc_offset": False, + }, + ), + ], +) +def test_date(model_date, db_date): + assert converters.date_to_db(model_date) == db_date + assert ( + converters.db_to_date( + date=None + if db_date["timestamp"] is None + else datetime.datetime.fromisoformat(db_date["timestamp"]), + offset=db_date["offset"], + neg_utc_offset=db_date["neg_utc_offset"], ) - ) == { - "timestamp": "2021-10-16T06:46:53+00:00", - "offset": -120, - "neg_utc_offset": False, - } + == model_date + ) def test_db_to_author():