From 41b135d180da4464df2ab51994f17005862dadf4 Mon Sep 17 00:00:00 2001 From: Martin Gallwey Date: Tue, 4 Aug 2026 17:15:07 +0100 Subject: [PATCH] Speed up date/timestamp encoding and type-code decoding Avoid use of pdcal for gregorian dates - speeds up date/timestamp values during insert. Speed up _getTypeCode by doing everything inline --- pynuodb/datatype.py | 18 ++++++++++++++++-- pynuodb/encodedsession.py | 12 +++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/pynuodb/datatype.py b/pynuodb/datatype.py index dca144d..7eb1a62 100644 --- a/pynuodb/datatype.py +++ b/pynuodb/datatype.py @@ -85,6 +85,20 @@ def timezone_aware(tstamp, tz_info): TICKSDAY = 86400 LOCALZONE = tzlocal.get_localzone() +# Fast path for ymd2day: for the Gregorian range (>= 1582-10-15) plain +# datetime.date subtraction (C-level) gives the identical result to the +# jdcal-based calendar.ymd2day, so avoid the pure-Python jdcal math there. +_EPOCH_DATE = Date(1970, 1, 1) +_GREGORIAN_START_DATE = Date(1582, 10, 15) + + +def _fast_ymd2day(year, month, day): + # type: (int, int, int) -> int + d = Date(year, month, day) + if d >= _GREGORIAN_START_DATE: + return (d - _EPOCH_DATE).days + return ymd2day(year, month, day) + try: LOCALZONE_NAME = tzlocal.get_localzone_name() # type: ignore except AttributeError: @@ -197,7 +211,7 @@ def TimestampFromTicks(ticks, micro=0, zoneinfo=LOCALZONE): def DateToTicks(value): # type: (Date) -> int """Convert a Date object to ticks.""" - day = ymd2day(value.year, value.month, value.day) + day = _fast_ymd2day(value.year, value.month, value.day) return day * TICKSDAY @@ -257,7 +271,7 @@ def TimestampToTicks(value, zoneinfo=LOCALZONE): if value.tzinfo is None: value = timezone_aware(value, zoneinfo) dt = value.astimezone(UTC) - timesecs = ymd2day(dt.year, dt.month, dt.day) * TICKSDAY + timesecs = _fast_ymd2day(dt.year, dt.month, dt.day) * TICKSDAY timesecs += dt.hour * 3600 timesecs += dt.minute * 60 timesecs += dt.second diff --git a/pynuodb/encodedsession.py b/pynuodb/encodedsession.py index e3b3ae1..bfa345a 100644 --- a/pynuodb/encodedsession.py +++ b/pynuodb/encodedsession.py @@ -1368,11 +1368,13 @@ def _peekTypeCode(self): def _getTypeCode(self): # type: () -> int - """Read the next Type Code off the session.""" - try: - return self._peekTypeCode() - finally: - self.__inpos += 1 + """Read the next Type Code off the session. Don't delegate to _peekTypeCode + for performance reasons.""" + inpos = self.__inpos + if inpos >= len(self.__input): + raise EndOfStream('end of stream reached') + self.__inpos = inpos + 1 + return self.__input[inpos] def _takeBytes(self, length): # type: (int) -> bytearray