From 4035b7dd517f90ea7fcbbfa4bd71660793589cb5 Mon Sep 17 00:00:00 2001 From: Rio Yu <52408936+rioyu123@users.noreply.github.com> Date: Sun, 4 Oct 2026 12:22:07 +0800 Subject: [PATCH] fix(google-api-core): preserve exact timestamp seconds --- .../google/api_core/datetime_helpers.py | 2 +- .../tests/unit/test_datetime_helpers.py | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/packages/google-api-core/google/api_core/datetime_helpers.py b/packages/google-api-core/google/api_core/datetime_helpers.py index 171a698d60c5..9ad5677311b7 100644 --- a/packages/google-api-core/google/api_core/datetime_helpers.py +++ b/packages/google-api-core/google/api_core/datetime_helpers.py @@ -268,7 +268,7 @@ def timestamp_pb(self): else self.replace(tzinfo=datetime.timezone.utc) ) delta = inst - _UTC_EPOCH - seconds = int(delta.total_seconds()) + seconds = delta.days * 86400 + delta.seconds nanos = self._nanosecond or self.microsecond * 1000 return timestamp_pb2.Timestamp(seconds=seconds, nanos=nanos) diff --git a/packages/google-api-core/tests/unit/test_datetime_helpers.py b/packages/google-api-core/tests/unit/test_datetime_helpers.py index b8f614af74db..ebe4e0ee9db9 100644 --- a/packages/google-api-core/tests/unit/test_datetime_helpers.py +++ b/packages/google-api-core/tests/unit/test_datetime_helpers.py @@ -349,6 +349,49 @@ def test_timestamp_pb_w_nanos(): ) assert stamp.timestamp_pb() == timestamp + @staticmethod + @pytest.mark.parametrize( + "value", + [ + "1969-12-31T23:59:59.123456789Z", + "1965-01-02T03:04:05.123456789Z", + "1969-12-31T23:59:59.000000001Z", + "1969-12-31T23:59:59Z", + "1970-01-01T00:00:00.123456789Z", + "2026-10-04T12:34:56.123456789Z", + "9999-12-31T23:59:59.999999999Z", + ], + ) + def test_timestamp_pb_preserves_whole_seconds(value): + stamp = datetime_helpers.DatetimeWithNanoseconds.from_rfc3339(value) + expected = timestamp_pb2.Timestamp() + expected.FromJsonString(value) + + assert stamp.timestamp_pb() == expected + + @staticmethod + @pytest.mark.parametrize( + "tzinfo", + [ + None, + datetime.timezone.utc, + datetime.timezone(datetime.timedelta(hours=5, minutes=30)), + datetime.timezone(datetime.timedelta(hours=-4)), + ], + ) + @pytest.mark.parametrize( + "fraction", [{"microsecond": 123456}, {"nanosecond": 123456789}] + ) + def test_timestamp_pb_fractional_seconds_with_timezone(tzinfo, fraction): + stamp = datetime_helpers.DatetimeWithNanoseconds( + 1969, 12, 31, 23, 59, 59, tzinfo=tzinfo, **fraction + ) + expected = timestamp_pb2.Timestamp() + expected.FromDatetime(stamp) + expected.nanos = fraction.get("nanosecond", stamp.microsecond * 1000) + + assert stamp.timestamp_pb() == expected + @staticmethod def test_from_timestamp_pb_wo_nanos(): when = datetime.datetime(