Skip to content

Commit a73076a

Browse files
fix(io): preserve acq_time timezone in scans.tsv repair
datetime.fromisoformat() parses RFC 3339 'Z' suffix as tz-aware UTC, but the strftime template used to renormalize the cell had no %z placeholder — so every load silently rewrote "...Z" to "..." and persisted the tz-stripped form back to *_scans.tsv. The "if normalized != ts" guard could not suppress this because strftime output structurally cannot contain 'Z', so every UTC-suffixed row took the "differ" branch. Split the normalization on dt.tzinfo: tz-aware inputs round-trip through "%Y-%m-%dT%H:%M:%S.%f%z" with the offset preserved (UTC restored to literal 'Z'; other offsets get ISO 8601 extended-form colon). Naive inputs and the unparseable -> "n/a" path are unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent dca5f09 commit a73076a

2 files changed

Lines changed: 37 additions & 2 deletions

File tree

‎eegdash/dataset/io.py‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1046,7 +1046,20 @@ def _repair_scans_tsv_timestamps(data_dir: Path) -> bool:
10461046
# and 6-digit microseconds — mne-bids uses
10471047
# strptime('%Y-%m-%dT%H:%M:%S.%f') which
10481048
# requires 'T' and fractional seconds.
1049-
normalized = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")
1049+
# For tz-aware inputs, preserve the tz: a
1050+
# plain "%Y-%m-%dT%H:%M:%S.%f" silently
1051+
# strips trailing 'Z' / '+HH:MM' offsets.
1052+
if dt.tzinfo is not None:
1053+
normalized = dt.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
1054+
# %z emits +HHMM; restore RFC 3339 'Z'
1055+
# for UTC, otherwise insert the ISO
1056+
# 8601 extended-form colon.
1057+
if normalized.endswith("+0000"):
1058+
normalized = normalized[:-5] + "Z"
1059+
else:
1060+
normalized = normalized[:-2] + ":" + normalized[-2:]
1061+
else:
1062+
normalized = dt.strftime("%Y-%m-%dT%H:%M:%S.%f")
10501063
if normalized != ts:
10511064
cols[acq_idx] = normalized
10521065
changed = True

‎tests/unit_tests/dataset/test_io.py‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,8 +385,30 @@ def test_read_bids_channels_tsv_parametrized(tmp_path, rows, expected):
385385
("2024-01-02 03:04:05", "2024-01-02T03:04:05.000000", True),
386386
("2024-01-02T03:04:99", "n/a", True),
387387
("2024-01-02T03:04:05.000000", "2024-01-02T03:04:05.000000", False),
388+
(
389+
"2024-01-02T03:04:05.000000Z",
390+
"2024-01-02T03:04:05.000000Z",
391+
False,
392+
),
393+
(
394+
"2024-01-02T03:04:05.000000+00:00",
395+
"2024-01-02T03:04:05.000000Z",
396+
True,
397+
),
398+
(
399+
"2024-01-02T03:04:05.000000-05:00",
400+
"2024-01-02T03:04:05.000000-05:00",
401+
False,
402+
),
403+
],
404+
ids=[
405+
"normalize_space_separator",
406+
"invalid_timestamp_to_na",
407+
"already_normalized",
408+
"preserve_z_suffix_utc",
409+
"normalize_plus_zero_offset_to_z",
410+
"preserve_negative_offset",
388411
],
389-
ids=["normalize_space_separator", "invalid_timestamp_to_na", "already_normalized"],
390412
)
391413
def test_repair_scans_tsv_timestamps_parametrized(
392414
tmp_path, acq_time, expected_time, expected_repaired

0 commit comments

Comments
 (0)