1717import flatbuffers .flexbuffers
1818from typing import Generic
1919import numpy as np
20- from math import floor
21- from datetime import datetime
20+ from datetime import datetime , timezone
2221from objectbox .c import *
2322from objectbox .model .properties import Property
23+ from objectbox .utils import date_value_to_int
2424import threading
2525
26+
2627# _Entity class holds model information as well as conversions between python objects and FlatBuffers (ObjectBox data)
2728class _Entity (object ):
2829 def __init__ (self , cls , id : int , uid : int ):
@@ -47,7 +48,7 @@ def __init__(self, cls, id: int, uid: int):
4748 self .id_property = None
4849 self .fill_properties ()
4950 self ._tl = threading .local ()
50-
51+
5152 def __call__ (self , ** properties ):
5253 """ The constructor of the user Entity class. """
5354 object_ = self .cls ()
@@ -122,9 +123,9 @@ def get_value(self, object, prop: Property):
122123 if (val == np .array (prop )).all ():
123124 return np .array ([])
124125 elif val == prop :
125- if prop ._py_type == datetime :
126- return datetime . fromtimestamp ( 0 )
127- if prop ._ob_type == OBXPropertyType_Flex :
126+ if prop ._ob_type == OBXPropertyType_Date or prop . _ob_type == OBXPropertyType_DateNano :
127+ return 0.0 # For marshalling, prefer float over datetime
128+ elif prop ._ob_type == OBXPropertyType_Flex :
128129 return None
129130 else :
130131 return prop ._py_type () # default (empty) value for the given type
@@ -186,13 +187,9 @@ def marshal(self, object, id: int) -> bytearray:
186187 else :
187188 val = id if prop == self .id_property else self .get_value (object , prop )
188189 if prop ._ob_type == OBXPropertyType_Date :
189- if prop ._py_type == datetime :
190- val = val .timestamp () * 1000 # timestamp returns seconds, convert to milliseconds
191- val = floor (val ) # use floor to allow for float types
190+ val = date_value_to_int (val , 1000 ) # convert to milliseconds
192191 elif prop ._ob_type == OBXPropertyType_DateNano :
193- if prop ._py_type == datetime :
194- val = val .timestamp () * 1000000000 # convert to nanoseconds
195- val = floor (val ) # use floor to allow for float types
192+ val = date_value_to_int (val , 1000000000 ) # convert to nanoseconds
196193 builder .Prepend (prop ._fb_type , val )
197194
198195 builder .Slot (prop ._fb_slot )
@@ -211,42 +208,49 @@ def unmarshal(self, data: bytes):
211208 for prop in self .properties :
212209 o = table .Offset (prop ._fb_v_offset )
213210 val = None
211+ ob_type = prop ._ob_type
214212 if not o :
215213 val = prop ._py_type () # use default (empty) value if not present in the object
216- elif prop . _ob_type == OBXPropertyType_String :
214+ elif ob_type == OBXPropertyType_String :
217215 val = table .String (o + table .Pos ).decode ('utf-8' )
218- elif prop . _ob_type == OBXPropertyType_BoolVector :
216+ elif ob_type == OBXPropertyType_BoolVector :
219217 val = table .GetVectorAsNumpy (flatbuffers .number_types .BoolFlags , o )
220- elif prop . _ob_type == OBXPropertyType_ByteVector :
218+ elif ob_type == OBXPropertyType_ByteVector :
221219 # access the FB byte vector information
222220 start = table .Vector (o )
223221 size = table .VectorLen (o )
224222 # slice the vector as a requested type
225- val = prop ._py_type (table .Bytes [start :start + size ])
226- elif prop . _ob_type == OBXPropertyType_ShortVector :
223+ val = prop ._py_type (table .Bytes [start :start + size ])
224+ elif ob_type == OBXPropertyType_ShortVector :
227225 val = table .GetVectorAsNumpy (flatbuffers .number_types .Int16Flags , o )
228- elif prop . _ob_type == OBXPropertyType_CharVector :
226+ elif ob_type == OBXPropertyType_CharVector :
229227 val = table .GetVectorAsNumpy (flatbuffers .number_types .Int16Flags , o )
230- elif prop ._ob_type == OBXPropertyType_Date and prop ._py_type == datetime :
231- table_val = table .Get (prop ._fb_type , o + table .Pos )
232- val = datetime .fromtimestamp (table_val / 1000 ) if table_val != 0 else datetime .fromtimestamp (0 ) # default timestamp
233- elif prop ._ob_type == OBXPropertyType_DateNano and prop ._py_type == datetime :
234- table_val = table .Get (prop ._fb_type , o + table .Pos )
235- val = datetime .fromtimestamp (table_val / 1000000000 ) if table_val != 0 else datetime .fromtimestamp (0 ) # default timestamp
236- elif prop ._ob_type == OBXPropertyType_IntVector :
228+ elif ob_type == OBXPropertyType_Date :
229+ val = table .Get (prop ._fb_type , o + table .Pos ) # int
230+ if prop ._py_type == datetime :
231+ val = datetime .fromtimestamp (val / 1000.0 , tz = timezone .utc )
232+ elif prop ._py_type == float :
233+ val = val / 1000.0
234+ elif ob_type == OBXPropertyType_DateNano and prop ._py_type == datetime :
235+ val = table .Get (prop ._fb_type , o + table .Pos ) # int
236+ if prop ._py_type == datetime :
237+ val = datetime .fromtimestamp (val / 1000000000.0 , tz = timezone .utc )
238+ elif prop ._py_type == float :
239+ val = val / 1000000000.0
240+ elif ob_type == OBXPropertyType_IntVector :
237241 val = table .GetVectorAsNumpy (flatbuffers .number_types .Int32Flags , o )
238- elif prop . _ob_type == OBXPropertyType_LongVector :
242+ elif ob_type == OBXPropertyType_LongVector :
239243 val = table .GetVectorAsNumpy (flatbuffers .number_types .Int64Flags , o )
240- elif prop . _ob_type == OBXPropertyType_FloatVector :
244+ elif ob_type == OBXPropertyType_FloatVector :
241245 val = table .GetVectorAsNumpy (flatbuffers .number_types .Float32Flags , o )
242- elif prop . _ob_type == OBXPropertyType_DoubleVector :
246+ elif ob_type == OBXPropertyType_DoubleVector :
243247 val = table .GetVectorAsNumpy (flatbuffers .number_types .Float64Flags , o )
244- elif prop . _ob_type == OBXPropertyType_Flex :
248+ elif ob_type == OBXPropertyType_Flex :
245249 # access the FB byte vector information
246250 start = table .Vector (o )
247251 size = table .VectorLen (o )
248252 # slice the vector as bytes
249- buf = table .Bytes [start :start + size ]
253+ buf = table .Bytes [start :start + size ]
250254 val = flatbuffers .flexbuffers .Loads (buf )
251255 else :
252256 val = table .Get (prop ._fb_type , o + table .Pos )
@@ -258,6 +262,8 @@ def unmarshal(self, data: bytes):
258262
259263def Entity (id : int = 0 , uid : int = 0 ) -> Callable [[Type ], _Entity ]:
260264 """ Entity decorator that wraps _Entity to allow @Entity(id=, uid=); i.e. no class arguments. """
265+
261266 def wrapper (class_ ):
262267 return _Entity (class_ , id , uid )
268+
263269 return wrapper
0 commit comments