Skip to content

Commit ca00e67

Browse files
Raise ValueError for naive datetime search attributes (#1882)
* Raise ValueError for naive datetime search attributes Mirror the untyped encoder timezone check into the typed path so upserting or encoding a datetime search attribute without tzinfo fails client-side instead of as a server BadSearchAttributes error. Signed-off-by: Kailash Nelson <37966146+KingLizard1020@users.noreply.github.com> * Move datetime changelog entry to Unreleased --------- Signed-off-by: Kailash Nelson <37966146+KingLizard1020@users.noreply.github.com> Co-authored-by: Tim Conley <tconley1428@gmail.com>
1 parent b773d5d commit ca00e67

3 files changed

Lines changed: 20 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ to include examples, links to docs, or any other relevant information.
4040

4141
### Fixed
4242

43+
- Encoding a datetime search attribute without a timezone now raises
44+
`ValueError("Timezone must be present on all search attribute dates")` on
45+
the typed path, matching the deprecated untyped encoder, instead of sending
46+
a naive ISO string that the server rejects with `BadSearchAttributes`.
47+
4348
- `temporalio.contrib.opentelemetry`: `TracingInterceptor` and `OpenTelemetryInterceptor` no longer
4449
log `Failed to detach context` when a context is torn down on a different thread while
4550
OpenTelemetry's threading instrumentation (enabled by strands, among others) is active; a

‎temporalio/converter/_search_attributes.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ def encode_typed_search_attribute_value(
6161
)
6262
# datetime needs to be in isoformat
6363
if isinstance(value, datetime):
64+
if value.tzinfo is None:
65+
raise ValueError("Timezone must be present on all search attribute dates")
6466
value = value.isoformat()
6567
# We'll do an extra sanity check for keyword list and check every value
6668
if isinstance(value, Sequence):

‎tests/test_converter.py‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
create_payload_validation_error,
5353
decode_search_attributes,
5454
encode_search_attribute_values,
55+
encode_typed_search_attribute_value,
5556
transfer_type_convertible,
5657
value_to_type,
5758
)
@@ -467,6 +468,18 @@ def test_encode_search_attribute_values():
467468
encode_search_attribute_values(["foo", 123]) # type: ignore[arg-type]
468469

469470

471+
def test_encode_typed_search_attribute_value_datetime_requires_timezone():
472+
key = temporalio.common.SearchAttributeKey.for_datetime("checkout_time")
473+
with pytest.raises(ValueError, match="Timezone must be present"):
474+
encode_typed_search_attribute_value(
475+
key, datetime(2024, 7, 5, 15, 43, 7, 875302)
476+
)
477+
payload = encode_typed_search_attribute_value(
478+
key, datetime(2024, 7, 5, 15, 43, 7, 875302, tzinfo=timezone.utc)
479+
)
480+
assert payload.metadata["type"] == b"Datetime"
481+
482+
470483
def test_decode_search_attributes():
471484
"""Tests decode from protobuf for python types"""
472485

0 commit comments

Comments
 (0)