11from __future__ import annotations
22
33import pickle
4+ from datetime import UTC , datetime , timedelta
45from typing import Any
56
67import google .protobuf .any_pb2
8+ import google .protobuf .duration_pb2
79import google .protobuf .empty_pb2
810import google .protobuf .message
11+ import google .protobuf .timestamp_pb2
912import google .protobuf .wrappers_pb2
1013from google .protobuf import descriptor_pool , message_factory
1114
@@ -30,6 +33,16 @@ def marshal_any(value: Any) -> google.protobuf.any_pb2.Any:
3033 value = google .protobuf .wrappers_pb2 .StringValue (value = value )
3134 elif isinstance (value , bytes ):
3235 value = google .protobuf .wrappers_pb2 .BytesValue (value = value )
36+ elif isinstance (value , datetime ):
37+ # Note: datetime only supports microsecond granularity
38+ seconds = int (value .timestamp ())
39+ nanos = value .microsecond * 1000
40+ value = google .protobuf .timestamp_pb2 .Timestamp (seconds = seconds , nanos = nanos )
41+ elif isinstance (value , timedelta ):
42+ # Note: timedelta only supports microsecond granularity
43+ seconds = int (value .total_seconds ())
44+ nanos = value .microseconds * 1000
45+ value = google .protobuf .duration_pb2 .Duration (seconds = seconds , nanos = nanos )
3346
3447 if not isinstance (value , google .protobuf .message .Message ):
3548 value = pickled_pb .Pickled (pickled_value = pickle .dumps (value ))
@@ -76,5 +89,9 @@ def unmarshal_any(any: google.protobuf.any_pb2.Any) -> Any:
7689 except Exception as e :
7790 # Otherwise, return the literal bytes.
7891 return proto .value
92+ elif isinstance (proto , google .protobuf .timestamp_pb2 .Timestamp ):
93+ return proto .ToDatetime (tzinfo = UTC )
94+ elif isinstance (proto , google .protobuf .duration_pb2 .Duration ):
95+ return proto .ToTimedelta ()
7996
8097 return proto
0 commit comments