From ce3c5d63c4a8d2531e977bbd3424cba8c6bcc73d Mon Sep 17 00:00:00 2001 From: Leonardo Scappatura <95079472+Leonard013@users.noreply.github.com> Date: Sat, 11 Jul 2026 21:07:59 +0200 Subject: [PATCH] Fix NixIO writing of datetime array_annotations NixIO._write_property passed datetime/date/time objects contained in array_annotations straight to nixio.create_property, which cannot store them and raised "Unknown type for value ...". Convert each datetime item to its ISO string form via dt_to_nix() on write (mirroring the existing scalar-annotation path) and record the annotation type as the property definition. On read, reconstruct multi-element datetime array_annotations element-wise into an object-dtype ndarray so the write->read roundtrip preserves them. Fixes #1198 Co-authored-by: Claude --- neo/io/nixio.py | 7 ++++++- neo/test/iotest/test_nixio.py | 23 +++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/neo/io/nixio.py b/neo/io/nixio.py index 2922138f8..19c991644 100644 --- a/neo/io/nixio.py +++ b/neo/io/nixio.py @@ -1261,6 +1261,8 @@ def _write_property(self, section, name, v): for item in v: if isinstance(item, str): item = item + elif isinstance(item, datetime_types): + item, definition = dt_to_nix(item) elif isinstance(item, pq.Quantity): current_unit = str(item.dimensionality) if unit is None: @@ -1320,7 +1322,10 @@ def _nix_attr_to_neo(nix_obj): units = prop.unit values = create_quantity(values, units) if prop.definition in (DATEANNOTATION, TIMEANNOTATION, DATETIMEANNOTATION): - values = dt_from_nix(values, prop.definition) + if isinstance(values, list): + values = np.array([dt_from_nix(v, prop.definition) for v in values], dtype=object) + else: + values = dt_from_nix(values, prop.definition) if prop.type == ARRAYANNOTATION: if "array_annotations" in neo_attrs: neo_attrs["array_annotations"][prop.name] = values diff --git a/neo/test/iotest/test_nixio.py b/neo/test/iotest/test_nixio.py index fdcf39529..2986ca660 100644 --- a/neo/test/iotest/test_nixio.py +++ b/neo/test/iotest/test_nixio.py @@ -1362,6 +1362,29 @@ def test_empty_array_annotations(self): else: self.assertFalse(hasattr(rst.array_annotations[k], "units")) + def test_datetime_array_annotations(self): + # Regression test for GH Issue #1198: array_annotations holding + # datetime/date/time objects must survive a NixIO write/read roundtrip + wblock = Block("block with datetime array annotations") + wseg = Segment() + times = np.array([1.0, 2.0, 3.0]) * pq.s + datetime_array_annotations = { + "acq_datetime": np.array( + [datetime(2021, 1, 1, 0, 0, 0), datetime(2021, 6, 15, 12, 30, 0), datetime(2022, 3, 3, 8, 15, 30)], + dtype=object, + ), + "acq_date": np.array([date(2021, 1, 1), date(2021, 6, 15), date(2022, 3, 3)], dtype=object), + "acq_time": np.array([time(0, 0, 0), time(12, 30, 0), time(8, 15, 30)], dtype=object), + } + wseg.events = [Event(times=times, array_annotations=datetime_array_annotations)] + wblock.segments = [wseg] + self.writer.write_block(wblock) + rblock = self.writer.read_block(neoname="block with datetime array annotations") + revent = rblock.segments[0].events[0] + for k, v in datetime_array_annotations.items(): + self.assertIn(k, revent.array_annotations) + np.testing.assert_array_equal(revent.array_annotations[k], v) + def test_write_proxyobjects(self): def generate_complete_block():