From f7c184ad4fd10a72f367769f03c2ac4f7cf1f5c8 Mon Sep 17 00:00:00 2001 From: Heberto Mayorquin Date: Thu, 25 Jun 2026 16:17:04 -0600 Subject: [PATCH] fix v1 date --- neo/rawio/axonrawio.py | 18 ++++++++++++++---- neo/test/rawiotest/test_axonrawio.py | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/neo/rawio/axonrawio.py b/neo/rawio/axonrawio.py index 166446a90..5b9d84f07 100644 --- a/neo/rawio/axonrawio.py +++ b/neo/rawio/axonrawio.py @@ -665,15 +665,24 @@ def _parse_abf_v1(f, header_description): header["sProtocolPath"] = header["sProtocolPath"].replace(b"\\", b"/") # date and time - YY = 1900 - MM = 1 - DD = 1 + # lFileStartDate is a YYYYMMDD-packed integer, parsed the same way as uFileStartDate in ABF2. + # An unset/sentinel value (0 or 0xFFFFFFFF) yields an out-of-range date that the try/except + # below turns into rec_datetime=None. + YY = int(header["lFileStartDate"] / 10000) + MM = int((header["lFileStartDate"] - YY * 10000) / 100) + DD = int(header["lFileStartDate"] - YY * 10000 - MM * 100) hh = int(header["lFileStartTime"] / 3600.0) mm = int((header["lFileStartTime"] - hh * 3600) / 60) ss = header["lFileStartTime"] - hh * 3600 - mm * 60 ms = int(np.mod(ss, 1) * 1e6) ss = int(ss) - header["rec_datetime"] = datetime.datetime(YY, MM, DD, hh, mm, ss, ms) + try: + header["rec_datetime"] = datetime.datetime(YY, MM, DD, hh, mm, ss, ms) + except (ValueError, OverflowError): + # Date/time header fields hold an out-of-range or "no date" sentinel + # (e.g. 0xFFFFFFFF). The acquisition date is non-essential annotation, + # so fall back to None rather than blocking the read of the signal. + header["rec_datetime"] = None return header @@ -1060,6 +1069,7 @@ def safe_decode_units(s): ("lActualAcqLength", 10, "i"), ("nNumPointsIgnored", 14, "h"), ("lActualEpisodes", 16, "i"), + ("lFileStartDate", 20, "i"), ("lFileStartTime", 24, "i"), ("lDataSectionPtr", 40, "i"), ("lTagSectionPtr", 44, "i"), diff --git a/neo/test/rawiotest/test_axonrawio.py b/neo/test/rawiotest/test_axonrawio.py index a1cb7dd2b..883b5406e 100644 --- a/neo/test/rawiotest/test_axonrawio.py +++ b/neo/test/rawiotest/test_axonrawio.py @@ -1,6 +1,7 @@ +import datetime import unittest -from neo.rawio.axonrawio import AxonRawIO +from neo.rawio.axonrawio import AxonRawIO, parse_axon_soup from neo.test.rawiotest.common_rawio_test import BaseTestRawIO @@ -28,6 +29,18 @@ def test_read_raw_protocol(self): reader.read_raw_protocol() + def test_v1_reads_real_acquisition_date(self): + # ABF1 stores the calendar date in lFileStartDate (a YYYYMMDD-packed integer). Older neo + # ignored that field and hardcoded 1900-01-01, so the recording date was always wrong for + # ABF1 files. It must now be read from the header. + expected_datetime = datetime.datetime(2005, 6, 11, 14, 15, 0) + header = parse_axon_soup(self.get_local_path("axon/File_axon_2.abf")) + rec_datetime = header["rec_datetime"] + # Drop sub-second precision: the millisecond field round-trips through a float, so the + # microsecond is a rounding artifact, not a meaningful value to assert. + self.assertEqual(rec_datetime.replace(microsecond=0), expected_datetime) + if __name__ == "__main__": unittest.main() + \ No newline at end of file