Skip to content

Commit 7dcad2b

Browse files
authored
Fix Python client CI: handle Timestamps outside Python stdlib range (#17715)
1 parent 3f4c44a commit 7dcad2b

1 file changed

Lines changed: 53 additions & 3 deletions

File tree

iotdb-client/client-py/iotdb/utils/rpc_utils.py

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,15 +79,34 @@ def verify_success_with_redirection_for_multi_devices(status: TSStatus, devices:
7979

8080
def convert_to_timestamp(time: int, precision: str, timezone: str):
8181
try:
82-
return pd.Timestamp(time, unit=precision, tz=timezone)
82+
ts = pd.Timestamp(time, unit=precision, tz=timezone)
8383
except OutOfBoundsDatetime:
84-
return pd.Timestamp(time, unit=precision).tz_localize(timezone)
84+
try:
85+
ts = pd.Timestamp(time, unit=precision).tz_localize(timezone)
86+
except NotImplementedError:
87+
return pd.Timestamp(time, unit=precision)
8588
except ValueError:
8689
logger.warning(
8790
f"Timezone string '{timezone}' cannot be recognized by pandas. "
8891
f"Falling back to local timezone: '{get_localzone_name()}'."
8992
)
90-
return pd.Timestamp(time, unit=precision, tz=get_localzone_name())
93+
ts = pd.Timestamp(time, unit=precision, tz=get_localzone_name())
94+
except NotImplementedError:
95+
# Timestamp falls outside Python's stdlib datetime range (year < 1 or
96+
# > 9999). pandas >= 3.0 cannot attach a timezone to such Timestamps
97+
# because tz conversion relies on toordinal(). Return naive instead.
98+
return pd.Timestamp(time, unit=precision)
99+
# Construction succeeded but utcoffset() may still fail downstream (e.g.
100+
# in pd.DataFrame's tz alignment) when the Timestamp's year is outside
101+
# 1..9999 and tz is not the canonical UTC. Probe early and fall back to
102+
# naive so callers like result_set_to_pandas() don't blow up. NaT does
103+
# not need this check (and does not support utcoffset()).
104+
if ts is not pd.NaT:
105+
try:
106+
ts.utcoffset()
107+
except NotImplementedError:
108+
return pd.Timestamp(time, unit=precision)
109+
return ts
91110

92111

93112
unit_map = {
@@ -108,3 +127,34 @@ def isoformat(ts: pd.Timestamp, unit: str):
108127
f"Falling back to use auto timespec'."
109128
)
110129
return ts.isoformat()
130+
except NotImplementedError:
131+
# Timestamp falls outside Python's stdlib datetime range. pandas >= 3.0
132+
# routes isoformat through datetime.isoformat, which calls toordinal()
133+
# and fails. Build the ISO 8601 string from individual components.
134+
return _isoformat_from_components(ts, unit)
135+
136+
137+
def _isoformat_from_components(ts: pd.Timestamp, unit: str) -> str:
138+
base = (
139+
f"{ts.year:04d}-{ts.month:02d}-{ts.day:02d}"
140+
f"T{ts.hour:02d}:{ts.minute:02d}:{ts.second:02d}"
141+
)
142+
if unit == "ms":
143+
base += f".{ts.microsecond // 1000:03d}"
144+
elif unit == "us":
145+
base += f".{ts.microsecond:06d}"
146+
elif unit == "ns":
147+
base += f".{ts.microsecond:06d}{ts.nanosecond:03d}"
148+
if ts.tzinfo is not None:
149+
try:
150+
offset = ts.utcoffset()
151+
except NotImplementedError:
152+
# utcoffset() needs toordinal() for zones like Etc/UTC. Omit offset.
153+
offset = None
154+
if offset is not None:
155+
total_seconds = int(offset.total_seconds())
156+
sign = "+" if total_seconds >= 0 else "-"
157+
total_seconds = abs(total_seconds)
158+
hh, mm = divmod(total_seconds // 60, 60)
159+
base += f"{sign}{hh:02d}:{mm:02d}"
160+
return base

0 commit comments

Comments
 (0)