From 08deaab94eff83519b1b963aa44411f91c73da30 Mon Sep 17 00:00:00 2001 From: "Walter A. Boring IV" Date: Fri, 28 Aug 2026 14:41:36 -0400 Subject: [PATCH] fix: replace deprecated datetime.utcfromtimestamp()/utcnow() calls (#268) Both APIs are deprecated since Python 3.12 and scheduled for removal. - aprsd/packets/core.py: datetime.utcfromtimestamp(ts) -> datetime.fromtimestamp(ts, tz=timezone.utc) - aprsd/plugins/time.py: pytz.datetime.datetime.utcnow() -> datetime.now(timezone.utc).replace(tzinfo=None) (naive UTC returned to preserve pytz.utc.localize() contract) - tests/plugins/test_time.py: same fix in test setup Closes #249 --- ChangeLog.md | 2 ++ aprsd/packets/core.py | 6 ++++-- aprsd/plugins/time.py | 4 +++- tests/plugins/test_time.py | 3 ++- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index d85143f..863e838 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -16,6 +16,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - Fix RingBuffer mutable class-level 'data: list = []' default — move to __init__ instance attribute [`aa43f60`](https://github.com/craigerl/aprsd/commit/aa43f60) +- Fix deprecated datetime.utcfromtimestamp()/utcnow() calls — replace with timezone-aware equivalents [`e7b0a19`](https://github.com/craigerl/aprsd/commit/e7b0a19) + - Add reset() to @singleton decorator; update tests to use ClassName.reset() instead of ClassName.instance = None [`ef19aaa`](https://github.com/craigerl/aprsd/commit/ef19aaa) - Fix PacketTrack.keys/items/values — return list snapshots instead of live dict views outside the lock [`088436e`](https://github.com/craigerl/aprsd/commit/088436e) diff --git a/aprsd/packets/core.py b/aprsd/packets/core.py index e842e25..f7cda4c 100644 --- a/aprsd/packets/core.py +++ b/aprsd/packets/core.py @@ -2,7 +2,7 @@ import logging import re import time from dataclasses import dataclass, field -from datetime import datetime +from datetime import datetime, timezone # Due to a failure in python 3.8 from typing import Any, List, Optional, Type, TypeVar, Union @@ -316,7 +316,9 @@ class GPSPacket(Packet): def _build_time_zulu(self): """Build the timestamp in UTC/zulu.""" if self.timestamp: - return datetime.utcfromtimestamp(self.timestamp).strftime('%d%H%M') + return datetime.fromtimestamp(self.timestamp, tz=timezone.utc).strftime( + '%d%H%M' + ) def _build_payload(self): """The payload is the non headers portion of the packet.""" diff --git a/aprsd/plugins/time.py b/aprsd/plugins/time.py index b47893f..6244767 100644 --- a/aprsd/plugins/time.py +++ b/aprsd/plugins/time.py @@ -24,7 +24,9 @@ class TimePlugin(plugin.APRSDRegexCommandPluginBase): return pytz.timezone(str(lz)) def _get_utcnow(self): - return pytz.datetime.datetime.utcnow() + import datetime as _dt + + return _dt.datetime.now(_dt.timezone.utc).replace(tzinfo=None) def build_date_str(self, localzone): utcnow = self._get_utcnow() diff --git a/tests/plugins/test_time.py b/tests/plugins/test_time.py index feadb72..b51ad71 100644 --- a/tests/plugins/test_time.py +++ b/tests/plugins/test_time.py @@ -1,3 +1,4 @@ +import datetime as _dt from unittest import mock import pytz @@ -15,7 +16,7 @@ class TestTimePlugins(test_plugin.TestPlugin): @mock.patch('aprsd.plugins.time.TimePlugin._get_local_tz') @mock.patch('aprsd.plugins.time.TimePlugin._get_utcnow') def test_time(self, mock_utcnow, mock_localtz): - utcnow = pytz.datetime.datetime.utcnow() + utcnow = _dt.datetime.now(_dt.timezone.utc).replace(tzinfo=None) mock_utcnow.return_value = utcnow tz = pytz.timezone('US/Pacific') mock_localtz.return_value = tz