From c1c33b7d4ed8d85a6c8dfbf1b8af8856794c34fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:44:24 +0000 Subject: [PATCH 1/2] Initial plan From e071a63c54949c9ed0e2eb8a2852fb969f5581e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 31 Mar 2026 12:50:29 +0000 Subject: [PATCH 2/2] refactor(s2): replace pytz with zoneinfo in s2 modules Agent-Logs-Url: https://github.com/FlexMeasures/flexmeasures-client/sessions/a3fc7d1d-eeb5-4f50-931b-d3c93a5d65a3 Co-authored-by: nhoening <1042336+nhoening@users.noreply.github.com> --- src/flexmeasures_client/s2/__init__.py | 4 ++-- .../s2/control_types/FRBC/frbc_simple.py | 7 +++--- .../s2/control_types/FRBC/frbc_tunes.py | 4 ++-- .../s2/script/websockets_client.py | 6 ++--- tests/s2/test_timezone.py | 22 +++++++++++++++++++ 5 files changed, 31 insertions(+), 12 deletions(-) create mode 100644 tests/s2/test_timezone.py diff --git a/src/flexmeasures_client/s2/__init__.py b/src/flexmeasures_client/s2/__init__.py index 02b50e87..981269e1 100644 --- a/src/flexmeasures_client/s2/__init__.py +++ b/src/flexmeasures_client/s2/__init__.py @@ -7,9 +7,9 @@ from dataclasses import dataclass from datetime import datetime from typing import Callable, Coroutine, Dict, Type +from zoneinfo import ZoneInfo import pydantic -import pytz try: from s2python.common import ReceptionStatus, ReceptionStatusValues, RevokeObject @@ -125,7 +125,7 @@ def __init__(self, max_size: int = 100, timezone: str = "UTC") -> None: self.outgoing_messages_status = SizeLimitOrderedDict(max_size=max_size) - self._timezone = pytz.timezone(timezone) + self._timezone = ZoneInfo(timezone) self.discover() diff --git a/src/flexmeasures_client/s2/control_types/FRBC/frbc_simple.py b/src/flexmeasures_client/s2/control_types/FRBC/frbc_simple.py index 6e81f291..698b8126 100644 --- a/src/flexmeasures_client/s2/control_types/FRBC/frbc_simple.py +++ b/src/flexmeasures_client/s2/control_types/FRBC/frbc_simple.py @@ -4,8 +4,7 @@ """ from datetime import datetime, timedelta - -import pytz +from zoneinfo import ZoneInfo try: from s2python.frbc import ( @@ -52,14 +51,14 @@ def __init__( self._schedule_duration = schedule_duration self._soc_sensor_id = soc_sensor_id self._rm_discharge_sensor_id = rm_discharge_sensor_id - self._timezone = pytz.timezone(timezone) + self._timezone = ZoneInfo(timezone) # delay the start of the schedule from the time `valid_from` # of the FRBC.SystemDescription. self._valid_from_shift = valid_from_shift def now(self): - return self._timezone.localize(datetime.now()) + return datetime.now(self._timezone) async def send_storage_status(self, status: FRBCStorageStatus): await self._fm_client.post_measurements( diff --git a/src/flexmeasures_client/s2/control_types/FRBC/frbc_tunes.py b/src/flexmeasures_client/s2/control_types/FRBC/frbc_tunes.py index 6b822791..e2496182 100644 --- a/src/flexmeasures_client/s2/control_types/FRBC/frbc_tunes.py +++ b/src/flexmeasures_client/s2/control_types/FRBC/frbc_tunes.py @@ -8,6 +8,7 @@ import json import math from datetime import datetime, timedelta +from zoneinfo import ZoneInfo import pandas as pd from requests.exceptions import HTTPError @@ -25,7 +26,6 @@ from typing import cast import pydantic -import pytz try: from s2python.common import ( @@ -153,7 +153,7 @@ def __init__( self._consumption_price_sensor_id = consumption_price_sensor self._production_price_sensor_id = production_price_sensor - self._timezone = pytz.timezone(timezone) + self._timezone = ZoneInfo(timezone) # delay the start of the schedule from the time `valid_from` of the FRBC.SystemDescription self._valid_from_shift = valid_from_shift diff --git a/src/flexmeasures_client/s2/script/websockets_client.py b/src/flexmeasures_client/s2/script/websockets_client.py index bf990c7b..c52910a3 100644 --- a/src/flexmeasures_client/s2/script/websockets_client.py +++ b/src/flexmeasures_client/s2/script/websockets_client.py @@ -1,8 +1,8 @@ import asyncio from datetime import datetime +from zoneinfo import ZoneInfo import aiohttp -import pytz from s2python.common import ( Commodity, CommodityQuantity, @@ -139,9 +139,7 @@ async def main_s2(): fill_level_range=NumberRange(start_of_range=0.05, end_of_range=0.45), ) - valid_from = pytz.timezone("Europe/Amsterdam").localize( - datetime(2026, 1, 15, 20) - ) + valid_from = datetime(2026, 1, 15, 20, tzinfo=ZoneInfo("Europe/Amsterdam")) system_description_message = FRBCSystemDescription( message_id=get_unique_id(), diff --git a/tests/s2/test_timezone.py b/tests/s2/test_timezone.py new file mode 100644 index 00000000..59ab27b6 --- /dev/null +++ b/tests/s2/test_timezone.py @@ -0,0 +1,22 @@ +from zoneinfo import ZoneInfo + +from flexmeasures_client.s2 import Handler +from flexmeasures_client.s2.control_types.FRBC.frbc_simple import FRBCSimple + + +def test_handler_uses_zoneinfo_timezone(): + handler = Handler(timezone="Europe/Amsterdam") + + assert handler.now().tzinfo == ZoneInfo("Europe/Amsterdam") + + +def test_frbc_simple_now_uses_zoneinfo_timezone(): + frbc = FRBCSimple( + power_sensor_id=1, + soc_sensor_id=2, + rm_discharge_sensor_id=3, + price_sensor_id=4, + timezone="Europe/Amsterdam", + ) + + assert frbc.now().tzinfo == ZoneInfo("Europe/Amsterdam")