2222
2323from __future__ import absolute_import
2424
25- import time
26- from datetime import datetime
25+ from datetime import date , time , datetime
2726try :
2827 from collections .abc import Iterable
2928except ImportError :
5554except NameError :
5655 string_types = str
5756
57+ datetime_types = (date , time , datetime )
58+
5859EMPTYANNOTATION = "EMPTYLIST"
5960ARRAYANNOTATION = "ARRAYANNOTATION"
61+ DATETIMEANNOTATION = "DATETIME"
62+ DATEANNOTATION = "DATE"
63+ TIMEANNOTATION = "TIME"
6064MIN_NIX_VER = Version ("1.5.0" )
6165
66+ datefmt = "%Y-%m-%d"
67+ timefmt = "%H:%M:%S.%f"
68+ datetimefmt = datefmt + "T" + timefmt
69+
6270
6371def stringify (value ):
6472 if value is None :
@@ -83,10 +91,40 @@ def units_to_string(pqunit):
8391 return dim
8492
8593
86- def calculate_timestamp (dt ):
94+ def dt_to_nix (dt ):
95+ """
96+ Converts date, time, and datetime objects to an ISO string representation
97+ appropriate for storing in NIX. Returns the converted value and the
98+ annotation type definition for converting back to the original value
99+ type.
100+ """
87101 if isinstance (dt , datetime ):
88- return int (time .mktime (dt .timetuple ()))
89- return int (dt )
102+ return dt .strftime (datetimefmt ), DATETIMEANNOTATION
103+ if isinstance (dt , date ):
104+ return dt .strftime (datefmt ), DATEANNOTATION
105+ if isinstance (dt , time ):
106+ return dt .strftime (timefmt ), TIMEANNOTATION
107+ # Unknown: returning as is
108+ return dt
109+
110+
111+ def dt_from_nix (nixdt , annotype ):
112+ """
113+ Inverse function of 'dt_to_nix()'. Requires the stored annotation type to
114+ distinguish between the three source types (date, time, and datetime).
115+ """
116+ if annotype == DATEANNOTATION :
117+ dt = datetime .strptime (nixdt , datefmt )
118+ return dt .date ()
119+ if annotype == TIMEANNOTATION :
120+ dt = datetime .strptime (nixdt , timefmt )
121+ return dt .time ()
122+ if annotype == DATETIMEANNOTATION :
123+ dt = datetime .strptime (nixdt , datetimefmt )
124+ return dt
125+ # Unknown type: older (or newer) IO version?
126+ # Returning as is to avoid data loss.
127+ return nixdt
90128
91129
92130def check_nix_version ():
@@ -307,7 +345,6 @@ def _nix_to_neo_segment(self, nix_group):
307345 neo_segment .rec_datetime = datetime .fromtimestamp (
308346 nix_group .created_at
309347 )
310-
311348 self ._neo_map [nix_group .name ] = neo_segment
312349
313350 # this will probably get all the DAs anyway, but if we change any part
@@ -582,12 +619,12 @@ def write_block(self, block, use_obj_names=False):
582619 metadata ["neo_name" ] = neoname
583620 nixblock .definition = block .description
584621 if block .rec_datetime :
585- nixblock .force_created_at (
586- calculate_timestamp (block .rec_datetime )
587- )
622+ nix_rec_dt = int (block .rec_datetime .strftime ("%s" ))
623+ nixblock .force_created_at (nix_rec_dt )
588624 if block .file_datetime :
589- fdt = calculate_timestamp (block .file_datetime )
590- metadata ["file_datetime" ] = fdt
625+ fdt , annotype = dt_to_nix (block .file_datetime )
626+ fdtprop = metadata .create_property ("file_datetime" , fdt )
627+ fdtprop .definition = annotype
591628 if block .annotations :
592629 for k , v in block .annotations .items ():
593630 self ._write_property (metadata , k , v )
@@ -683,12 +720,12 @@ def _write_segment(self, segment, nixblock):
683720 metadata ["neo_name" ] = neoname
684721 nixgroup .definition = segment .description
685722 if segment .rec_datetime :
686- nixgroup .force_created_at (
687- calculate_timestamp (segment .rec_datetime )
688- )
723+ nix_rec_dt = int (segment .rec_datetime .strftime ("%s" ))
724+ nixgroup .force_created_at (nix_rec_dt )
689725 if segment .file_datetime :
690- fdt = calculate_timestamp (segment .file_datetime )
691- metadata ["file_datetime" ] = fdt
726+ fdt , annotype = dt_to_nix (segment .file_datetime )
727+ fdtprop = metadata .create_property ("file_datetime" , fdt )
728+ fdtprop .definition = annotype
692729 if segment .annotations :
693730 for k , v in segment .annotations .items ():
694731 self ._write_property (metadata , k , v )
@@ -1161,8 +1198,10 @@ def _write_property(self, section, name, v):
11611198 else :
11621199 section .create_property (name , v .magnitude .item ())
11631200 section .props [name ].unit = str (v .dimensionality )
1164- elif isinstance (v , datetime ):
1165- section .create_property (name , calculate_timestamp (v ))
1201+ elif isinstance (v , datetime_types ):
1202+ value , annotype = dt_to_nix (v )
1203+ prop = section .create_property (name , value )
1204+ prop .definition = annotype
11661205 elif isinstance (v , string_types ):
11671206 if len (v ):
11681207 section .create_property (name , v )
@@ -1201,8 +1240,7 @@ def _write_property(self, section, name, v):
12011240 values .append (item )
12021241 section .create_property (name , values )
12031242 section .props [name ].unit = unit
1204- if definition :
1205- section .props [name ].definition = definition
1243+ section .props [name ].definition = definition
12061244 elif type (v ).__module__ == "numpy" :
12071245 section .create_property (name , v .item ())
12081246 else :
@@ -1237,6 +1275,9 @@ def _nix_attr_to_neo(nix_obj):
12371275 values = ""
12381276 elif len (values ) == 1 :
12391277 values = values [0 ]
1278+ if prop .definition in (DATEANNOTATION , TIMEANNOTATION ,
1279+ DATETIMEANNOTATION ):
1280+ values = dt_from_nix (values , prop .definition )
12401281 if prop .definition == ARRAYANNOTATION :
12411282 if 'array_annotations' in neo_attrs :
12421283 neo_attrs ['array_annotations' ][prop .name ] = values
@@ -1248,10 +1289,6 @@ def _nix_attr_to_neo(nix_obj):
12481289 # there's no reason to keep it in the annotations
12491290 neo_attrs ["name" ] = stringify (neo_attrs .pop ("neo_name" , None ))
12501291
1251- if "file_datetime" in neo_attrs :
1252- neo_attrs ["file_datetime" ] = datetime .fromtimestamp (
1253- neo_attrs ["file_datetime" ]
1254- )
12551292 return neo_attrs
12561293
12571294 @staticmethod
@@ -1283,9 +1320,7 @@ def _get_time_dimension(obj):
12831320 return None
12841321
12851322 def _use_obj_names (self , blocks ):
1286-
12871323 errmsg = "use_obj_names enabled: found conflict or anonymous object"
1288-
12891324 allobjs = []
12901325
12911326 def check_unique (objs ):
0 commit comments