Skip to content

Commit 540da39

Browse files
committed
Enforce UTC validation for timestamps
Now timestamps have to be specified in UTC. If not, the client will raise an error. Converting timezones doesn't have to be part of the thin client and can be provided in a higher-level client if needed. Signed-off-by: Sahas Subramanian <[email protected]>
1 parent ca3bb56 commit 540da39

File tree

2 files changed

+44
-61
lines changed

2 files changed

+44
-61
lines changed

src/frequenz/client/electricity_trading/_types.py

Lines changed: 29 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ class DeliveryPeriod:
386386
duration: DeliveryDuration
387387
"""The length of the delivery period."""
388388

389+
def __post_init__(self) -> None:
390+
"""Validate that the start timestamp is in UTC."""
391+
if self.start.tzinfo is None or self.start.tzinfo != timezone.utc:
392+
raise ValueError("Start timestamp must be in UTC timezone.")
389393

390394
def __eq__(
391395
self,
@@ -951,11 +955,11 @@ class Order: # pylint: disable=too-many-instance-attributes
951955
def __post_init__(self) -> None:
952956
"""Post initialization checks to ensure that all datetimes are UTC."""
953957
if self.valid_until is not None:
954-
if self.valid_until.tzinfo is None:
958+
if (
959+
self.valid_until.tzinfo is None
960+
or self.valid_until.tzinfo != timezone.utc
961+
):
955962
raise ValueError("Valid until must be a UTC datetime.")
956-
if self.valid_until.tzinfo != timezone.utc:
957-
_logger.warning("Valid until is not a UTC datetime. Converting to UTC.")
958-
self.valid_until = self.valid_until.astimezone(timezone.utc)
959963

960964
@classmethod
961965
@from_pb
@@ -1124,11 +1128,11 @@ class Trade: # pylint: disable=too-many-instance-attributes
11241128

11251129
def __post_init__(self) -> None:
11261130
"""Post initialization checks to ensure that all datetimes are UTC."""
1127-
if self.execution_time.tzinfo is None:
1128-
raise ValueError("Execution time must have timezone information")
1129-
if self.execution_time.tzinfo != timezone.utc:
1130-
_logger.warning("Execution timenis not in UTC timezone. Converting to UTC.")
1131-
self.execution_time = self.execution_time.astimezone(timezone.utc)
1131+
if (
1132+
self.execution_time.tzinfo is None
1133+
or self.execution_time.tzinfo != timezone.utc
1134+
):
1135+
raise ValueError("Execution time must be a UTC datetime.")
11321136

11331137
@classmethod
11341138
@from_pb
@@ -1255,19 +1259,14 @@ def __post_init__(self) -> None:
12551259
ValueError: If create_time or modification_time do not have timezone information.
12561260
12571261
"""
1258-
if self.create_time.tzinfo is None:
1259-
raise ValueError("Create time must have timezone information")
1260-
if self.create_time.tzinfo != timezone.utc:
1261-
_logger.warning("Create time is not in UTC timezone. Converting to UTC.")
1262-
self.create_time = self.create_time.astimezone(timezone.utc)
1262+
if self.create_time.tzinfo is None or self.create_time.tzinfo != timezone.utc:
1263+
raise ValueError("Create time must be a UTC datetime.")
12631264

1264-
if self.modification_time.tzinfo is None:
1265-
raise ValueError("Modification time must have timezone information")
1266-
if self.modification_time.tzinfo != timezone.utc:
1267-
_logger.warning(
1268-
"Modification time is not in UTC timezone. Converting to UTC."
1269-
)
1270-
self.modification_time = self.modification_time.astimezone(timezone.utc)
1265+
if (
1266+
self.modification_time.tzinfo is None
1267+
or self.modification_time.tzinfo != timezone.utc
1268+
):
1269+
raise ValueError("Modification time must be a UTC datetime.")
12711270

12721271
@classmethod
12731272
@from_pb
@@ -1346,11 +1345,11 @@ class PublicTrade: # pylint: disable=too-many-instance-attributes
13461345

13471346
def __post_init__(self) -> None:
13481347
"""Post initialization checks to ensure that all datetimes are UTC."""
1349-
if self.execution_time.tzinfo is None:
1350-
raise ValueError("Execution time must have timezone information")
1351-
if self.execution_time.tzinfo != timezone.utc:
1352-
_logger.warning("Execution time is not in UTC timezone. Converting to UTC.")
1353-
self.execution_time = self.execution_time.astimezone(timezone.utc)
1348+
if (
1349+
self.execution_time.tzinfo is None
1350+
or self.execution_time.tzinfo != timezone.utc
1351+
):
1352+
raise ValueError("Execution time must be a UTC datetime.")
13541353

13551354
@classmethod
13561355
@from_pb
@@ -1796,12 +1795,10 @@ class UpdateOrder: # pylint: disable=too-many-instance-attributes
17961795

17971796
def __post_init__(self) -> None:
17981797
"""Post initialization checks to ensure that all datetimes are UTC."""
1799-
if self.valid_until is not None:
1800-
if self.valid_until.tzinfo is None:
1801-
raise ValueError("Valid until must be a UTC datetime.")
1802-
if self.valid_until.tzinfo != timezone.utc:
1803-
_logger.warning("Valid until is not a UTC datetime. Converting to UTC.")
1804-
self.valid_until = self.valid_until.astimezone(timezone.utc)
1798+
if self.valid_until is not None and (
1799+
self.valid_until.tzinfo is None or self.valid_until.tzinfo != timezone.utc
1800+
):
1801+
raise ValueError("Valid until must be a UTC datetime.")
18051802

18061803
@classmethod
18071804
@from_pb
@@ -1929,18 +1926,6 @@ class PublicOrder: # pylint: disable=too-many-instance-attributes
19291926
update_time: datetime
19301927
"""UTC Timestamp of the last update to the order."""
19311928

1932-
def __post_init__(self) -> None:
1933-
"""Post initialization checks to ensure that all datetimes are UTC."""
1934-
if self.delivery_period.start.tzinfo is None:
1935-
raise ValueError("Delivery period start must have timezone information")
1936-
if self.delivery_period.start.tzinfo != timezone.utc:
1937-
_logger.warning(
1938-
"Delivery period start is not in UTC timezone. Converting to UTC."
1939-
)
1940-
self.delivery_period.start = self.delivery_period.start.astimezone(
1941-
timezone.utc
1942-
)
1943-
19441929
@classmethod
19451930
@from_pb
19461931
def from_pb(

tests/test_types.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -605,25 +605,23 @@ def test_order_detail_no_timezone_error() -> None:
605605
)
606606

607607

608-
def test_order_detail_timezone_converted_to_utc() -> None:
608+
def test_order_detail_non_utc_timezone() -> None:
609609
"""Test that an order detail with inputs with non-UTC timezone is converted to UTC."""
610610
start = datetime.now(timezone(timedelta(hours=1)))
611-
order_detail = OrderDetail(
612-
order_id=1,
613-
order=ORDER,
614-
state_detail=StateDetail(
615-
state=OrderState.ACTIVE,
616-
state_reason=StateReason.ADD,
617-
market_actor=MarketActor.USER,
618-
),
619-
open_quantity=Power(mw=Decimal("5.00")),
620-
filled_quantity=Power(mw=Decimal("0.00")),
621-
create_time=start,
622-
modification_time=start,
623-
)
624-
625-
assert order_detail.create_time.tzinfo == timezone.utc
626-
assert order_detail.modification_time.tzinfo == timezone.utc
611+
with pytest.raises(ValueError):
612+
OrderDetail(
613+
order_id=1,
614+
order=ORDER,
615+
state_detail=StateDetail(
616+
state=OrderState.ACTIVE,
617+
state_reason=StateReason.ADD,
618+
market_actor=MarketActor.USER,
619+
),
620+
open_quantity=Power(mw=Decimal("5.00")),
621+
filled_quantity=Power(mw=Decimal("0.00")),
622+
create_time=start,
623+
modification_time=start,
624+
)
627625

628626

629627
def test_gridpool_order_filter_to_pb() -> None:

0 commit comments

Comments
 (0)