Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit 79e5259

Browse files
authored
fix: add timezone to timestamps (#64)
* fix: add timezone to timestamps * chore: version bump
1 parent 473f620 commit 79e5259

File tree

3 files changed

+73
-2
lines changed

3 files changed

+73
-2
lines changed

eppo_client/client.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def get_assignment_detail(
222222
"featureFlag": flag_key,
223223
"variation": result.variation.key if result and result.variation else None,
224224
"subject": subject_key,
225-
"timestamp": datetime.datetime.utcnow().isoformat(),
225+
"timestamp": _utcnow().isoformat(),
226226
"subjectAttributes": subject_attributes,
227227
"metaData": {"sdkLanguage": "python", "sdkVersion": __version__},
228228
}
@@ -370,7 +370,7 @@ def evaluate_bandit_action(
370370
"modelVersion": (
371371
bandit_data.bandit_model_version if evaluation else None
372372
),
373-
"timestamp": datetime.datetime.utcnow().isoformat(),
373+
"timestamp": _utcnow().isoformat(),
374374
"subjectNumericAttributes": (
375375
subject_context_attributes.numeric_attributes
376376
if evaluation.subject_attributes
@@ -492,3 +492,7 @@ def convert_actions_to_action_contexts(
492492
actions: Union[ActionContexts, ActionAttributes]
493493
) -> ActionContexts:
494494
return {k: convert_attributes_to_context_attributes(v) for k, v in actions.items()}
495+
496+
497+
def _utcnow() -> datetime.datetime:
498+
return datetime.datetime.now(datetime.timezone.utc)

test/client_bandit_test.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# making client_test.py too long.
33

44

5+
import datetime
56
import json
67
import os
78
from time import sleep
@@ -156,6 +157,37 @@ def test_get_bandit_action_bandit_error(mock_bandit_evaluator):
156157
assert len(mock_assignment_logger.bandit_events) == 0
157158

158159

160+
def test_bandit_event_has_utc_timestamp():
161+
# tests that allocation filtering based on subject attributes works correctly
162+
client = get_instance()
163+
actions = {
164+
"adidas": ContextAttributes(
165+
numeric_attributes={"discount": 0.1},
166+
categorical_attributes={"from": "germany"},
167+
),
168+
"nike": ContextAttributes(
169+
numeric_attributes={"discount": 0.2}, categorical_attributes={"from": "usa"}
170+
),
171+
}
172+
client.get_bandit_action(
173+
"banner_bandit_flag_uk_only",
174+
"alice",
175+
DEFAULT_SUBJECT_ATTRIBUTES,
176+
actions,
177+
"default_variation",
178+
)
179+
180+
# testing assignment logger
181+
assignment_event = mock_assignment_logger.assignment_events[-1]
182+
timestamp = datetime.datetime.fromisoformat(assignment_event["timestamp"])
183+
assert timestamp.tzinfo == datetime.timezone.utc
184+
185+
# testing bandit logger
186+
bandit_event = mock_assignment_logger.bandit_events[-1]
187+
timestamp = datetime.datetime.fromisoformat(bandit_event["timestamp"])
188+
assert timestamp.tzinfo == datetime.timezone.utc
189+
190+
159191
def test_get_bandit_action_with_subject_attributes():
160192
# tests that allocation filtering based on subject attributes works correctly
161193
client = get_instance()

test/client_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import json
2+
import datetime
23
import os
34
from time import sleep
45
from unittest.mock import patch
@@ -113,6 +114,40 @@ def test_log_assignment(mock_config_requestor, mock_logger):
113114
assert mock_logger.log_assignment.call_count == 1
114115

115116

117+
@patch("eppo_client.assignment_logger.AssignmentLogger")
118+
@patch("eppo_client.configuration_requestor.ExperimentConfigurationRequestor")
119+
def test_assignment_event_has_utc_timestamp(mock_config_requestor, mock_logger):
120+
flag = Flag(
121+
key="flag-key",
122+
enabled=True,
123+
variation_type=VariationType.STRING,
124+
variations={"control": Variation(key="control", value="control")},
125+
allocations=[
126+
Allocation(
127+
key="allocation",
128+
splits=[
129+
Split(
130+
variation_key="control",
131+
shards=[Shard(salt="salt", ranges=[Range(start=0, end=10000)])],
132+
)
133+
],
134+
do_log=True,
135+
)
136+
],
137+
total_shards=10_000,
138+
)
139+
140+
mock_config_requestor.get_configuration.return_value = flag
141+
client = EppoClient(
142+
config_requestor=mock_config_requestor, assignment_logger=mock_logger
143+
)
144+
client.get_string_assignment("falg-key", "user-1", {}, "default value")
145+
146+
event = mock_logger.log_assignment.call_args.args[0]
147+
timestamp = datetime.datetime.fromisoformat(event["timestamp"])
148+
assert timestamp.tzinfo == datetime.timezone.utc
149+
150+
116151
@patch("eppo_client.assignment_logger.AssignmentLogger")
117152
@patch("eppo_client.configuration_requestor.ExperimentConfigurationRequestor")
118153
def test_get_assignment_handles_logging_exception(mock_config_requestor, mock_logger):

0 commit comments

Comments
 (0)