Skip to content

Commit 51daf38

Browse files
author
Chris Rossi
authored
fix: Handle int for DateTimeProperty (#285)
In Datastore, projection queries involving entities with DateTime properties return integer timestamps instead of `datetime.datetime` objects. This fix handles that case and returns `datetime.datetime` objects regardless of the query type. Fixes #261.
1 parent 4a1429c commit 51daf38

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

packages/google-cloud-ndb/google/cloud/ndb/model.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3601,14 +3601,22 @@ def _from_base_type(self, value):
36013601
"""Convert a value from the "base" value type for this property.
36023602
36033603
Args:
3604-
value (datetime.datetime): The value to be converted.
3604+
value (Union[int, datetime.datetime]): The value to be converted.
3605+
The value will be `int` for entities retrieved by a projection
3606+
query and is a timestamp as the number of nanoseconds since the
3607+
epoch.
36053608
36063609
Returns:
36073610
Optional[datetime.datetime]: If ``tzinfo`` is set on this property,
36083611
the value converted to the timezone in ``tzinfo``. Otherwise
36093612
returns the value without ``tzinfo`` or ``None`` if value did
36103613
not have ``tzinfo`` set.
36113614
"""
3615+
if isinstance(value, six.integer_types):
3616+
# Projection query, value is integer nanoseconds
3617+
seconds = value / 1e6
3618+
value = datetime.datetime.fromtimestamp(seconds, pytz.utc)
3619+
36123620
if self._tzinfo is not None:
36133621
return value.astimezone(self._tzinfo)
36143622

packages/google-cloud-ndb/tests/system/test_query.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616
System tests for queries.
1717
"""
1818

19+
import datetime
1920
import functools
2021
import operator
2122

2223
import grpc
2324
import pytest
25+
import pytz
2426

2527
import test_utils.system
2628

@@ -194,6 +196,38 @@ class SomeKind(ndb.Model):
194196
results[1].bar
195197

196198

199+
@pytest.mark.usefixtures("client_context")
200+
def test_projection_datetime(ds_entity):
201+
"""Regression test for Issue #261
202+
203+
https://github.com/googleapis/python-ndb/issues/261
204+
"""
205+
entity_id = test_utils.system.unique_resource_id()
206+
ds_entity(
207+
KIND,
208+
entity_id,
209+
foo=datetime.datetime(2010, 5, 12, 2, 42, tzinfo=pytz.UTC),
210+
)
211+
entity_id = test_utils.system.unique_resource_id()
212+
ds_entity(
213+
KIND,
214+
entity_id,
215+
foo=datetime.datetime(2010, 5, 12, 2, 43, tzinfo=pytz.UTC),
216+
)
217+
218+
class SomeKind(ndb.Model):
219+
foo = ndb.DateTimeProperty()
220+
bar = ndb.StringProperty()
221+
222+
query = SomeKind.query(projection=("foo",))
223+
results = eventually(query.fetch, _length_equals(2))
224+
225+
results = sorted(results, key=operator.attrgetter("foo"))
226+
227+
assert results[0].foo == datetime.datetime(2010, 5, 12, 2, 42)
228+
assert results[1].foo == datetime.datetime(2010, 5, 12, 2, 43)
229+
230+
197231
@pytest.mark.usefixtures("client_context")
198232
def test_distinct_on(ds_entity):
199233
for i in range(6):

packages/google-cloud-ndb/tests/unit/test_model.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,6 +2707,14 @@ def test__from_base_type_convert_timezone():
27072707
2010, 5, 11, 20, tzinfo=timezone(-4)
27082708
)
27092709

2710+
@staticmethod
2711+
def test__from_base_type_int():
2712+
prop = model.DateTimeProperty(name="dt_val")
2713+
value = 1273632120000000
2714+
assert prop._from_base_type(value) == datetime.datetime(
2715+
2010, 5, 12, 2, 42
2716+
)
2717+
27102718
@staticmethod
27112719
def test__to_base_type_noop():
27122720
prop = model.DateTimeProperty(name="dt_val", tzinfo=timezone(-4))

0 commit comments

Comments
 (0)