Repository: mne-tools/mne-python

Issue:
Negative meas_date in gdf2 example
I was not expecting 'meas_date' to be negative. Is this a bug? or that this example indeed has negative meas_date?

#### Steps and/or code to reproduce
```py
import os.path as op
from mne.io import read_raw_edf
from mne.annotations import _handle_meas_date
from mne.datasets import testing

data_path = testing.data_path(download=False)
gdf2_path = op.join(data_path, 'GDF', 'test_gdf_2.20')

raw = read_raw_edf(gdf2_path + '.gdf', eog=None, misc=None, preload=True,
                    stim_channel='STATUS')
assert _handle_meas_date(raw.info['meas_date']) > 0
```




Discussion:
pb is here:

https://github.com/mne-tools/mne-python/blob/master/mne/io/edf/edf.py#L899

if date is not present we use:

date = datetime.datetime(1, 1, 1)

and then we use:

meas_date=(calendar.timegm(date.utctimetuple()), 0)

which leads to a negative meas_date....

basically we can fix this as it's negative as we fixed a bad date by default.
We should change an unknown date to be DATE_NONE
+1

can you give it a try?


Resolving patch:
diff --git a/mne/io/base.py b/mne/io/base.py
--- a/mne/io/base.py
+++ b/mne/io/base.py
@@ -467,6 +467,7 @@ def _read_segment(self, start=0, stop=None, sel=None, data_buffer=None,
 
         #  Initialize the data and calibration vector
         n_sel_channels = self.info['nchan'] if sel is None else len(sel)
+        assert n_sel_channels <= self.info['nchan']
         # convert sel to a slice if possible for efficiency
         if sel is not None and len(sel) > 1 and np.all(np.diff(sel) == 1):
             sel = slice(sel[0], sel[-1] + 1)
@@ -831,6 +832,7 @@ def _parse_get_set_params(self, item):
                 stop += nchan
                 if stop < 0:
                     raise ValueError('stop must be >= -%s' % nchan)
+            stop = min(stop, nchan)  # slices can legally exceed max
             step = item[0].step if item[0].step is not None else 1
             sel = list(range(start, stop, step))
         else:
diff --git a/mne/io/edf/edf.py b/mne/io/edf/edf.py
--- a/mne/io/edf/edf.py
+++ b/mne/io/edf/edf.py
@@ -17,7 +17,7 @@
 from ...utils import verbose, logger, warn
 from ..utils import _blk_read_lims
 from ..base import BaseRaw, _check_update_montage
-from ..meas_info import _empty_info
+from ..meas_info import _empty_info, DATE_NONE
 from ..constants import FIFF
 from ...filter import resample
 from ...externals.six.moves import zip
@@ -553,6 +553,8 @@ def _get_info(fname, stim_channel, annot, annotmap, eog, misc, exclude,
         info['highpass'] = float(np.max(highpass))
         warn('Channels contain different highpass filters. Highest filter '
              'setting will be stored.')
+    if np.isnan(info['highpass']):
+        info['highpass'] = 0.
     if lowpass.size == 0:
         pass  # Placeholder for future use. Lowpass set in _empty_info.
     elif all(lowpass):
@@ -564,6 +566,8 @@ def _get_info(fname, stim_channel, annot, annotmap, eog, misc, exclude,
         info['lowpass'] = float(np.min(lowpass))
         warn('Channels contain different lowpass filters. Lowest filter '
              'setting will be stored.')
+    if np.isnan(info['lowpass']):
+        info['lowpass'] = info['sfreq'] / 2.
 
     # Some keys to be consistent with FIF measurement info
     info['description'] = None
@@ -605,6 +609,7 @@ def _read_edf_header(fname, annot, annotmap, exclude):
                              re.findall(r'(\d+)', fid.read(8).decode())]
         century = 2000 if year < 50 else 1900
         date = datetime.datetime(year + century, month, day, hour, minute, sec)
+        meas_date = (calendar.timegm(date.utctimetuple()), 0)
 
         header_nbytes = int(fid.read(8).decode())
 
@@ -665,8 +670,7 @@ def _read_edf_header(fname, annot, annotmap, exclude):
         edf_info.update(
             ch_names=ch_names, data_offset=header_nbytes,
             digital_max=digital_max, digital_min=digital_min,
-            highpass=highpass, sel=sel, lowpass=lowpass,
-            meas_date=(calendar.timegm(date.utctimetuple()), 0),
+            highpass=highpass, sel=sel, lowpass=lowpass, meas_date=meas_date,
             n_records=n_records, n_samps=n_samps, nchan=nchan,
             subject_info=patient, physical_max=physical_max,
             physical_min=physical_min, record_length=record_length,
@@ -716,6 +720,7 @@ def _read_gdf_header(fname, stim_channel, exclude):
 
         edf_info['type'] = edf_info['subtype'] = version[:3]
         edf_info['number'] = float(version[4:])
+        meas_date = DATE_NONE
 
         # GDF 1.x
         # ---------------------------------------------------------------------
@@ -738,12 +743,13 @@ def _read_gdf_header(fname, stim_channel, exclude):
             try:
                 if tm[14:16] == '  ':
                     tm = tm[:14] + '00' + tm[16:]
-                date = (datetime.datetime(int(tm[0:4]), int(tm[4:6]),
-                                          int(tm[6:8]), int(tm[8:10]),
-                                          int(tm[10:12]), int(tm[12:14]),
-                                          int(tm[14:16]) * pow(10, 4)))
+                date = datetime.datetime(int(tm[0:4]), int(tm[4:6]),
+                                         int(tm[6:8]), int(tm[8:10]),
+                                         int(tm[10:12]), int(tm[12:14]),
+                                         int(tm[14:16]) * pow(10, 4))
+                meas_date = (calendar.timegm(date.utctimetuple()), 0)
             except Exception:
-                date = datetime.datetime(2000, 1, 1)
+                pass
 
             header_nbytes = np.fromfile(fid, np.int64, 1)[0]
             meas_id['equipment'] = np.fromfile(fid, np.uint8, 8)[0]
@@ -805,7 +811,7 @@ def _read_gdf_header(fname, stim_channel, exclude):
                 dtype_byte=[gdftype_byte[t] for t in dtype],
                 dtype_np=[gdftype_np[t] for t in dtype], exclude=exclude,
                 highpass=highpass, sel=sel, lowpass=lowpass,
-                meas_date=(calendar.timegm(date.utctimetuple()), 0),
+                meas_date=meas_date,
                 meas_id=meas_id, n_records=n_records, n_samps=n_samps,
                 nchan=nchan, subject_info=patient, physical_max=physical_max,
                 physical_min=physical_min, record_length=record_length,
@@ -896,11 +902,10 @@ def _read_gdf_header(fname, stim_channel, exclude):
             meas_id['loc'] = loc
 
             date = np.fromfile(fid, np.uint64, 1)[0]
-            if date == 0:
-                date = datetime.datetime(1, 1, 1)
-            else:
+            if date != 0:
                 date = datetime.datetime(1, 1, 1) + \
                     datetime.timedelta(date * pow(2, -32) - 367)
+                meas_date = (calendar.timegm(date.utctimetuple()), 0)
 
             birthday = np.fromfile(fid, np.uint64, 1).tolist()[0]
             if birthday == 0:
@@ -1027,8 +1032,7 @@ def _read_gdf_header(fname, stim_channel, exclude):
                 dtype_np=[gdftype_np[t] for t in dtype],
                 digital_min=digital_min, digital_max=digital_max,
                 exclude=exclude, gnd=gnd, highpass=highpass, sel=sel,
-                impedance=impedance, lowpass=lowpass,
-                meas_date=(calendar.timegm(date.utctimetuple()), 0),
+                impedance=impedance, lowpass=lowpass, meas_date=meas_date,
                 meas_id=meas_id, n_records=n_records, n_samps=n_samps,
                 nchan=nchan, notch=notch, subject_info=patient,
                 physical_max=physical_max, physical_min=physical_min,
diff --git a/mne/io/meas_info.py b/mne/io/meas_info.py
--- a/mne/io/meas_info.py
+++ b/mne/io/meas_info.py
@@ -127,7 +127,7 @@ class Info(dict):
         Name of the person that ran the experiment.
     file_id : dict | None
         The FIF globally unique ID. See Notes for more information.
-    highpass : float | None
+    highpass : float
         Highpass corner frequency in Hertz. Zero indicates a DC recording.
     hpi_meas : list of dict
         HPI measurements that were taken at the start of the recording
@@ -145,7 +145,7 @@ class Info(dict):
         Frequency of the power line in Hertz.
     gantry_angle : float | None
         Tilt angle of the gantry in degrees.
-    lowpass : float | None
+    lowpass : float
         Lowpass corner frequency in Hertz.
     meas_date : tuple of int
         The first element of this list is a UNIX timestamp (seconds since


Test patch:
diff --git a/mne/io/edf/tests/test_gdf.py b/mne/io/edf/tests/test_gdf.py
--- a/mne/io/edf/tests/test_gdf.py
+++ b/mne/io/edf/tests/test_gdf.py
@@ -13,6 +13,8 @@
 
 from mne.datasets import testing
 from mne.io import read_raw_edf
+from mne.io.meas_info import DATE_NONE
+from mne.io.tests.test_raw import _test_raw_reader
 from mne.utils import run_tests_if_main
 from mne import pick_types, find_events
 
@@ -48,6 +50,12 @@ def test_gdf_data():
     evs = raw.find_edf_events()
     assert (all([event in evs[1] for event in events[:, 0]]))
 
+    # gh-5604
+    assert raw.info['meas_date'] == DATE_NONE
+    with pytest.warns(RuntimeWarning, match='Overlapping events'):
+        _test_raw_reader(read_raw_edf, input_fname=gdf1_path + '.gdf',
+                         eog=None, misc=None, stim_channel='auto')
+
 
 @testing.requires_testing_data
 def test_gdf2_data():
@@ -80,5 +88,10 @@ def test_gdf2_data():
     assert_equal(nchan, raw.info['nchan'])  # stim channel not constructed
     assert_array_equal(ch_names[1:], raw.ch_names[1:])
 
+    # gh-5604
+    assert raw.info['meas_date'] == DATE_NONE
+    _test_raw_reader(read_raw_edf, input_fname=gdf2_path + '.gdf',
+                     eog=None, misc=None, stim_channel='STATUS')
+
 
 run_tests_if_main()
diff --git a/mne/io/tests/test_raw.py b/mne/io/tests/test_raw.py
--- a/mne/io/tests/test_raw.py
+++ b/mne/io/tests/test_raw.py
@@ -8,6 +8,7 @@
                            assert_equal, assert_array_equal)
 
 from mne import concatenate_raws, create_info, Annotations
+from mne.annotations import _handle_meas_date
 from mne.datasets import testing
 from mne.io import read_raw_fif, RawArray
 from mne.utils import _TempDir
@@ -62,6 +63,9 @@ def _test_raw_reader(reader, test_preloading=True, **kwargs):
     assert raw.__class__.__name__ in repr(raw)  # to test repr
     assert raw.info.__class__.__name__ in repr(raw.info)
 
+    # gh-5604
+    assert _handle_meas_date(raw.info['meas_date']) >= 0
+
     # Test saving and reading
     out_fname = op.join(tempdir, 'test_raw.fif')
     raw = concatenate_raws([raw])