Skip to content

Commit 3a3d34b

Browse files
committed
Can store Date as datetime #6
1 parent bed337e commit 3a3d34b

File tree

4 files changed

+129
-7
lines changed

4 files changed

+129
-7
lines changed

objectbox/model/entity.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import flatbuffers
1717
import numpy as np
1818
from math import floor
19+
from datetime import datetime
1920
from objectbox.c import *
2021
from objectbox.model.properties import Property
2122

@@ -89,8 +90,11 @@ def get_value(self, object, prop: Property):
8990
if prop._py_type == np.ndarray:
9091
if (val == np.array(prop)).all():
9192
return np.array([])
93+
elif prop._py_type == datetime:
94+
if val == prop:
95+
return datetime.fromtimestamp(0)
9296
elif val == prop:
93-
return prop._py_type() # default (empty) value for the given type
97+
return prop._py_type() # default (empty) value for the given type
9498
return val
9599

96100
def get_object_id(self, object) -> int:
@@ -131,9 +135,14 @@ def marshal(self, object, id: int) -> bytearray:
131135
if val:
132136
builder.PrependUOffsetTRelative(val)
133137
else:
134-
val = id if prop == self.id_property else self.get_value(
135-
object, prop)
136-
if prop._ob_type == OBXPropertyType_Date or prop._ob_type == OBXPropertyType_DateNano:
138+
val = id if prop == self.id_property else self.get_value(object, prop)
139+
if prop._ob_type == OBXPropertyType_Date:
140+
if prop._py_type == datetime:
141+
val = val.timestamp() * 1000 # timestamp returns seconds, convert to milliseconds
142+
val = floor(val) # use floor to allow for float types
143+
elif prop._ob_type == OBXPropertyType_DateNano:
144+
if prop._py_type == datetime:
145+
val = val.timestamp() * 1000000000 # convert to nanoseconds
137146
val = floor(val) # use floor to allow for float types
138147
builder.Prepend(prop._fb_type, val)
139148

@@ -163,6 +172,12 @@ def unmarshal(self, data: bytes):
163172
size = table.VectorLen(o)
164173
# slice the vector as a requested type
165174
val = prop._py_type(table.Bytes[start:start+size])
175+
elif prop._ob_type == OBXPropertyType_Date and prop._py_type == datetime:
176+
table_val = table.Get(prop._fb_type, o + table.Pos)
177+
val = datetime.fromtimestamp(table_val/1000) if table_val != 0 else datetime.fromtimestamp(0) # default timestamp
178+
elif prop._ob_type == OBXPropertyType_DateNano and prop._py_type == datetime:
179+
table_val = table.Get(prop._fb_type, o + table.Pos)
180+
val = datetime.fromtimestamp(table_val/1000000000) if table_val != 0 else datetime.fromtimestamp(0) # default timestamp
166181
elif prop._ob_type == OBXPropertyType_IntVector:
167182
val = table.GetVectorAsNumpy(flatbuffers.number_types.Int32Flags, o)
168183
elif prop._ob_type == OBXPropertyType_LongVector:

tests/common.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import shutil
44
import pytest
5-
from tests.model import TestEntity
5+
from tests.model import TestEntity, TestEntityDatetime
66
import numpy as np
77

88
test_dir = 'testdata'
@@ -32,6 +32,17 @@ def load_empty_test_objectbox(name: str = "") -> objectbox.ObjectBox:
3232
return objectbox.Builder().model(model).directory(db_name).build()
3333

3434

35+
def load_empty_test_datetime(name: str = "") -> objectbox.ObjectBox:
36+
model = objectbox.Model()
37+
from objectbox.model import IdUid
38+
model.entity(TestEntityDatetime, last_property_id=IdUid(3, 1003))
39+
model.last_entity_id = IdUid(1, 1)
40+
41+
db_name = test_dir if len(name) == 0 else test_dir + "/" + name
42+
43+
return objectbox.Builder().model(model).directory(db_name).build()
44+
45+
3546
def assert_equal_prop(actual, expected, default):
3647
assert actual == expected or (isinstance(
3748
expected, objectbox.model.Property) and actual == default)

tests/model.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from objectbox.model import *
22
import numpy as np
3+
from datetime import datetime
34

45

56
@Entity(id=1, uid=1)
@@ -28,3 +29,12 @@ class TestEntity:
2829

2930
def __init__(self, string: str = ""):
3031
self.str = string
32+
33+
@Entity(id=1, uid=1)
34+
class TestEntityDatetime:
35+
id = Id(id=1, uid=1001)
36+
date = Property(datetime, type=PropertyType.date, id=2, uid=1002)
37+
date_nano = Property(datetime, type=PropertyType.dateNano, id=3, uid=1003)
38+
39+
def __init__(self, string: str = ""):
40+
self.str = string

tests/test_box.py

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import pytest
22
import objectbox
3+
from tests.model import TestEntity, TestEntityDatetime
4+
from tests.common import (
5+
autocleanup,
6+
load_empty_test_objectbox,
7+
load_empty_test_datetime,
8+
assert_equal,
9+
)
310
import numpy as np
4-
from tests.model import TestEntity
5-
from tests.common import autocleanup, load_empty_test_objectbox, assert_equal
11+
from datetime import datetime
612
import time
713
from math import floor
814

@@ -110,3 +116,83 @@ def test_box_bulk():
110116
removed = box.remove_all()
111117
assert removed == 4
112118
assert box.count() == 0
119+
120+
121+
def test_datetime():
122+
ob = load_empty_test_datetime()
123+
box = objectbox.Box(ob, TestEntityDatetime)
124+
125+
assert box.is_empty()
126+
assert box.count() == 0
127+
128+
# create
129+
object = TestEntityDatetime()
130+
id = box.put(object)
131+
assert id == 1
132+
assert id == object.id
133+
134+
# create with a given ID and some data
135+
object = TestEntityDatetime()
136+
object.id = 5
137+
object.date = datetime.utcnow() # milliseconds since UNIX epoch
138+
object.date_nano = datetime.utcnow() # nanoseconds since UNIX epoch
139+
140+
id = box.put(object)
141+
assert id == 5
142+
assert id == object.id
143+
# check the count
144+
assert not box.is_empty()
145+
assert box.count() == 2
146+
147+
# read
148+
read = box.get(object.id)
149+
assert pytest.approx(read.date.timestamp()) == object.date.timestamp()
150+
151+
# update
152+
object.str = "bar"
153+
object.date = datetime.utcnow()
154+
object.date_nano = datetime.utcnow()
155+
id = box.put(object)
156+
assert id == 5
157+
158+
# read again
159+
read = box.get(object.id)
160+
assert pytest.approx(read.date.timestamp()) == object.date.timestamp()
161+
162+
# remove
163+
box.remove(object)
164+
box.remove(1)
165+
166+
# check they're gone
167+
assert box.count() == 0
168+
with pytest.raises(objectbox.NotFoundException):
169+
box.get(object.id)
170+
with pytest.raises(objectbox.NotFoundException):
171+
box.get(1)
172+
173+
174+
def test_box_bulk_datetime():
175+
ob = load_empty_test_datetime()
176+
box = objectbox.Box(ob, TestEntityDatetime)
177+
178+
box.put(TestEntityDatetime("first"))
179+
180+
objects = [TestEntityDatetime("second"), TestEntityDatetime("third"),
181+
TestEntityDatetime("fourth"), box.get(1)]
182+
box.put(objects)
183+
assert box.count() == 4
184+
assert objects[0].id == 2
185+
assert objects[1].id == 3
186+
assert objects[2].id == 4
187+
assert objects[3].id == 1
188+
189+
objects_read = box.get_all()
190+
assert len(objects_read) == 4
191+
for object_read in objects_read:
192+
assert object_read.date == datetime.fromtimestamp(0)
193+
assert object_read.date_nano == datetime.fromtimestamp(0)
194+
195+
# remove all
196+
removed = box.remove_all()
197+
assert removed == 4
198+
assert box.count() == 0

0 commit comments

Comments
 (0)