You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by fo...@apache.org on 2026/08/15 20:34:30 UTC
(iceberg-python) branch main updated: fix: pad sub-microsecond digits when parsing nanosecond (#3614)
This is an automated email from the ASF dual-hosted git repository.
Fokko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-python.git
The following commit(s) were added to refs/heads/main by this push:
new ccefb86d7 fix: pad sub-microsecond digits when parsing nanosecond (#3614)
ccefb86d7 is described below
commit ccefb86d7e7eab5ceaf8d531a993929f80851e51
Author: Anas Khan <an...@gmail.com>
AuthorDate: Sun Aug 16 02:04:24 2026 +0530
fix: pad sub-microsecond digits when parsing nanosecond (#3614)
timestamp_to_nanos and timestamptz_to_nanos read the sub-microsecond
digits of the fraction (regex group 3 of ISO_TIMESTAMP_NANO /
ISO_TIMESTAMPTZ_NANO) as a bare integer. For a 7- or 8-digit fraction
that group is 1 or 2 digits and represents positions 7-8 of the
fraction, so it must be right-padded to 3 digits before being
interpreted as nanoseconds. Without padding, .1234567 was parsed as
+7 ns instead of +700 ns, silently corrupting the value.
Right-pad the group to 3 digits with ljust so all fraction widths
round-trip correctly, and add 7- and 8-digit cases to the tests, which
previously only covered 6- and 9-digit fractions.
Signed-off-by: Anas Khan <83...@users.noreply.github.com>
---
pyiceberg/utils/datetime.py | 8 ++++++--
tests/utils/test_datetime.py | 4 ++++
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/pyiceberg/utils/datetime.py b/pyiceberg/utils/datetime.py
index e5fa2595f..ea7329ea2 100644
--- a/pyiceberg/utils/datetime.py
+++ b/pyiceberg/utils/datetime.py
@@ -121,7 +121,9 @@ def timestamp_to_nanos(timestamp_str: str) -> int:
if match := ISO_TIMESTAMP_NANO.fullmatch(timestamp_str):
# Python datetime does not have native nanoseconds support
# Hence we need to extract nanoseconds timestamp manually
- ns_str = match.group(3) or "0"
+ # group(3) holds the sub-microsecond digits (fraction positions 7-9), so
+ # right-pad to 3 digits before reading them as nanoseconds (e.g. "7" -> 700).
+ ns_str = (match.group(3) or "0").ljust(3, "0")
ms_str = match.group(2) if match.group(2) else ""
timestamp_str_without_ns_str = match.group(1) + ms_str
return datetime_to_nanos(datetime.fromisoformat(timestamp_str_without_ns_str)) + int(ns_str)
@@ -136,7 +138,9 @@ def timestamptz_to_nanos(timestamptz_str: str) -> int:
if match := ISO_TIMESTAMPTZ_NANO.fullmatch(timestamptz_str):
# Python datetime does not have native nanoseconds support
# Hence we need to extract nanoseconds timestamp manually
- ns_str = match.group(3) or "0"
+ # group(3) holds the sub-microsecond digits (fraction positions 7-9), so
+ # right-pad to 3 digits before reading them as nanoseconds (e.g. "7" -> 700).
+ ns_str = (match.group(3) or "0").ljust(3, "0")
ms_str = match.group(2) if match.group(2) else ""
timestamptz_str_without_ns_str = match.group(1) + ms_str + match.group(4)
return datetime_to_nanos(datetime.fromisoformat(timestamptz_str_without_ns_str)) + int(ns_str)
diff --git a/tests/utils/test_datetime.py b/tests/utils/test_datetime.py
index e9529b6d3..d7a6f431e 100644
--- a/tests/utils/test_datetime.py
+++ b/tests/utils/test_datetime.py
@@ -111,6 +111,8 @@ def test_datetime_to_nanos(datetime_: datetime, nanos: int) -> None:
[
("1970-01-01T00:00:00", 0),
("2025-02-23T20:21:44.375612", 1740342104375612000),
+ ("2025-02-23T20:21:44.3756127", 1740342104375612700),
+ ("2025-02-23T20:21:44.37561278", 1740342104375612780),
("2025-02-23T20:21:44.375612001", 1740342104375612001),
],
)
@@ -128,6 +130,8 @@ def test_timestamp_to_nanos_unexpected_zone_offset() -> None:
[
("1970-01-01T00:00:00+00:00", 0),
("2025-02-23T16:21:44.375612-04:00", 1740342104375612000),
+ ("2025-02-23T16:21:44.3756127-04:00", 1740342104375612700),
+ ("2025-02-23T16:21:44.37561278-04:00", 1740342104375612780),
("2025-02-23T16:21:44.375612001-04:00", 1740342104375612001),
],
)