Repository: NeurodataWithoutBorders/pynwb

Issue:
[Bug]: Fail to read NWB file where TimeZone is missing from session_start_time
### What happened?

Not sure if this is a bug, but it seems strict to enforce TimeZone on read.

### Steps to Reproduce

In MATLAB:
```matlab
nwb = NwbFile( ...
    'session_description', 'Simple demo file', ...
    'identifier', 'test_file', ...
    'session_start_time', datetime("now"), ...
    'timestamps_reference_time', datetime("now"));
nwbExport(nwb, 'temp.nwb')
```

In python:
```python
from pynwb import NWBHDF5IO

io = NWBHDF5IO('temp.nwb', mode="r")
nwbfile = io.read()
```

### Traceback

```python
Error using objectmapper>construct
Python Error: ConstructError: (root GroupBuilder {'attributes': {'namespace': 'core', 'neurodata_type': 'NWBFile', 'nwb_version': '2.8.0',
'object_id': 'ad86c34b-4de3-4352-a698-d112fe26f032'}, 'groups': {'acquisition': root/acquisition GroupBuilder {'attributes': {}, 'groups': {},
'datasets': {}, 'links': {}}, 'analysis': root/analysis GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}, 'general':
root/general GroupBuilder {'attributes': {}, 'groups': {'devices': root/general/devices GroupBuilder {'attributes': {}, 'groups': {},
'datasets': {}, 'links': {}}, 'extracellular_ephys': root/general/extracellular_ephys GroupBuilder {'attributes': {}, 'groups': {},
'datasets': {}, 'links': {}}, 'intracellular_ephys': root/general/intracellular_ephys GroupBuilder {'attributes': {}, 'groups': {},
'datasets': {}, 'links': {}}, 'optogenetics': root/general/optogenetics GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links':
{}}, 'optophysiology': root/general/optophysiology GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}}, 'datasets':
{}, 'links': {}}, 'intervals': root/intervals GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}, 'processing':
root/processing GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}, 'scratch': root/scratch GroupBuilder
{'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}, 'stimulus': root/stimulus GroupBuilder {'attributes': {}, 'groups':
{'presentation': root/stimulus/presentation GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}, 'templates':
root/stimulus/templates GroupBuilder {'attributes': {}, 'groups': {}, 'datasets': {}, 'links': {}}}, 'datasets': {}, 'links': {}}},
'datasets': {'file_create_date': root/file_create_date DatasetBuilder {'attributes': {}, 'data': <StrDataset for HDF5 dataset
"file_create_date": shape (1,), type "|O">}, 'identifier': root/identifier DatasetBuilder {'attributes': {}, 'data': 'test_file'},
'session_description': root/session_description DatasetBuilder {'attributes': {}, 'data': 'Simple demo file'}, 'session_start_time':
root/session_start_time DatasetBuilder {'attributes': {}, 'data': '2025-03-05T15:08:07.380042'}, 'timestamps_reference_time':
root/timestamps_reference_time DatasetBuilder {'attributes': {}, 'data': '2025-03-05T15:08:07.380880'}}, 'links': {}}, "Could not construct
NWBFile object due to: 'timestamps_reference_time' must be a timezone-aware datetime object.")
```

### Operating System

macOS

### Python Executable

Python

### Python Version

3.9

### Package Versions

_No response_

### Code of Conduct

- [x] I agree to follow this project's [Code of Conduct](https://github.com/NeurodataWithoutBorders/pynwb/blob/dev/.github/CODE_OF_CONDUCT.rst)
- [x] Have you checked the [Contributing](https://github.com/NeurodataWithoutBorders/pynwb/blob/dev/docs/CONTRIBUTING.rst) document?
- [x] Have you ensured this bug was not already [reported](https://github.com/NeurodataWithoutBorders/pynwb/issues)?

Discussion hints:

Solution patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 52c00b21..d4f6ad56 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ## Upcoming
 
 ### Enhancements and minor changes
+- Automatically add timezone information to timestamps reference time if no timezone information is specified. @stephprince [#2056](https://github.com/NeurodataWithoutBorders/pynwb/pull/2056)
 - Added option to disable typemap caching and updated type map cache location. @stephprince [#2057](https://github.com/NeurodataWithoutBorders/pynwb/pull/2057)
 - Added dictionary-like operations directly on `ProcessingModule` objects (e.g., `len(processing_module)`). @bendichter [#2020](https://github.com/NeurodataWithoutBorders/pynwb/pull/2020)
 - When an external file is detected when initializing an ImageSeries and no format is provided, automatically set format to "external" instead of raising an error. @stephprince [#2060](https://github.com/NeurodataWithoutBorders/pynwb/pull/2060)
diff --git a/src/pynwb/file.py b/src/pynwb/file.py
index d0a0e617..33369904 100644
--- a/src/pynwb/file.py
+++ b/src/pynwb/file.py
@@ -491,7 +491,7 @@ class NWBFile(MultiContainerInterface, HERDManager):
         if timestamps_reference_time is None:
             args_to_set['timestamps_reference_time'] = args_to_set['session_start_time']
         elif timestamps_reference_time.tzinfo is None:
-            raise ValueError("'timestamps_reference_time' must be a timezone-aware datetime object.")
+            args_to_set['timestamps_reference_time'] = _add_missing_timezone(timestamps_reference_time)
 
         # convert file_create_date to list and add timezone if missing
         file_create_date = args_to_set['file_create_date']


Test patch:
diff --git a/tests/unit/test_file.py b/tests/unit/test_file.py
index c76abd7f..7d66926c 100644
--- a/tests/unit/test_file.py
+++ b/tests/unit/test_file.py
@@ -698,13 +698,14 @@ class TestTimestampsRefAware(TestCase):
         self.ref_time_notz = datetime(1979, 1, 1, 0, 0, 0)
 
     def test_reftime_tzaware(self):
-        with self.assertRaises(ValueError):
-            # 'timestamps_reference_time' must be a timezone-aware datetime
-            NWBFile('test session description',
-                    'TEST124',
-                    self.start_time,
-                    timestamps_reference_time=self.ref_time_notz)
+        with self.assertWarnsWith(UserWarning, "Date is missing timezone information. Updating to local timezone."):
+            nwbfile = NWBFile('test session description',
+                              'TEST124',
+                              self.start_time,
+                              timestamps_reference_time=self.ref_time_notz)
 
+        # test time zone is automatically added to timestamps_reference_time
+        self.assertEqual(nwbfile.timestamps_reference_time, self.ref_time_notz.replace(tzinfo=tzlocal()))
 
 class TestTimezone(TestCase):
     def test_raise_warning__add_missing_timezone(self):