From cfcf00162f717c1b0fea8599d1ab71da29bbec04 Mon Sep 17 00:00:00 2001 From: Zo Bot Date: Fri, 10 Jul 2026 23:38:31 +0000 Subject: [PATCH] is_timestamp: reject non-finite floats so 'inf' and 'nan' no longer look like timestamps --- arrow/util.py | 14 +++++++++++--- tests/test_util.py | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/arrow/util.py b/arrow/util.py index 7171d92cc..1e341df0c 100644 --- a/arrow/util.py +++ b/arrow/util.py @@ -1,6 +1,7 @@ """Helpful functions used internally within arrow.""" import datetime +import math from typing import Any, Optional from dateutil.rrule import WEEKLY, rrule @@ -43,16 +44,23 @@ def next_weekday( def is_timestamp(value: Any) -> bool: - """Check if value is a valid timestamp.""" + """Check if value is a valid timestamp. + + A value is considered a valid timestamp when it is an ``int``, ``float``, + or ``str`` that parses via :func:`float` to a finite real number. ``bool`` + is rejected (it is technically an ``int`` subclass). Non-finite floats — + ``inf``, ``-inf``, and ``nan`` — are rejected because they cannot be + converted into a real datetime. + """ if isinstance(value, bool): return False if not isinstance(value, (int, float, str)): return False try: - float(value) - return True + parsed = float(value) except ValueError: return False + return math.isfinite(parsed) def validate_ordinal(value: Any) -> None: diff --git a/tests/test_util.py b/tests/test_util.py index 2454dac56..fbb28edfd 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -56,6 +56,21 @@ class InvalidTimestamp: full_datetime = "2019-06-23T13:12:42" assert not util.is_timestamp(full_datetime) + def test_is_timestamp_rejects_non_finite(self): + # Non-finite floats (inf/-inf/nan) cannot be turned into a real + # datetime, so is_timestamp must not treat them as a valid timestamp + # and let them reach the regex- or float()-driven downstream paths. + assert not util.is_timestamp(float("inf")) + assert not util.is_timestamp(float("-inf")) + assert not util.is_timestamp(float("nan")) + assert not util.is_timestamp("inf") + assert not util.is_timestamp("-inf") + assert not util.is_timestamp("nan") + assert not util.is_timestamp("Infinity") + assert not util.is_timestamp("NaN") + # A literal that overflows float() into +inf must be rejected too. + assert not util.is_timestamp("1e1000") + def test_validate_ordinal(self): timestamp_float = 1607066816.815537 timestamp_int = int(timestamp_float)