Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions arrow/util.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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:
Expand Down
15 changes: 15 additions & 0 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading