Repository: mne-tools/mne-python

Issue:
[BUG] set_annotations fails for data with a meas_date prior to 1970
Try reading this [example_file](https://drive.google.com/file/d/1vZCW3wUQ_0gemac_NFO9LtYVlG70rt-R/view?usp=sharing) (800MB) and check out `raw.info['meas_date']` --> it will be negative.

That's because the data has been anonymized (cc @agramfort ) to some time prior 1970.

That's an issue, because we represent `raw.info['meas_date']` in unix time, which is 0 at 1970, and thus negative for all prior years (strictly speaking, it's even undefined)

A negative `meas_date` will raise an error when we make an annotation object from scratch and try to set it with `raw.set_annotations()`.

That is because of this code:

in `set_annotations`: https://github.com/mne-tools/mne-python/blob/368a490f9d7a298050ee04c2aa7882629c9fab21/mne/io/base.py#L792

--> `time_of_first_sample` will be negative if `meas_date` is negative

then later we do: https://github.com/mne-tools/mne-python/blob/368a490f9d7a298050ee04c2aa7882629c9fab21/mne/io/base.py#L798-L801

i.e., pass a negative `tmin` to the `crop` function, which then raises an error: https://github.com/mne-tools/mne-python/blob/368a490f9d7a298050ee04c2aa7882629c9fab21/mne/annotations.py#L363-L364

cc @massich 


Discussion:
If there is no limitations with fif files to have negative time stamps we should handle these files smoothly 

Yes this seems like a bug in our Annotations handling somewhere. @massich can you look?

It should not matter what `meas_date` relative to 1970 is (negative or positive)
yes I was just looking at it
What was the logic behind `DATE_NONE` and anonymization? 

I understand that is a convention to move the date to 1930 or so for anonymization purposes. But do people usually keep the rest of the information i.e: imagine that I have one record every month/year for a particular subject. Do they only offset to 1/1/1930 and keep all the recordings with respect to that date or do we fold all recordings to the same date?
@massich see: https://github.com/mne-tools/mne-bids/issues/193#issue-436776387

I don't know about how anonymization is currently handled in MNE, but I think the way we SHOULD handle this is to allow for an anonymization by a certain offset in days ... this would then be applied to all data recordings of a subject, so that the relative time distances keep their logic.

the `anonymization_offset_days` would be set to a sensible default but could also be given as an input to `raw.annonymize(anonymization_offset_days=36500)`

I briefly discussed this with @larsoner at the last MNE sprint, maybe he wants to weigh in

Resolving patch:
diff --git a/mne/annotations.py b/mne/annotations.py
--- a/mne/annotations.py
+++ b/mne/annotations.py
@@ -350,8 +350,8 @@ def crop(self, tmin=None, tmax=None, emit_warning=False):
         if len(self) == 0:
             return  # no annotations, nothing to do
 
-        offset = 0 if self.orig_time is None else self.orig_time
-        absolute_onset = self.onset + offset
+        orig_time_offset = 0 if self.orig_time is None else self.orig_time
+        absolute_onset = self.onset + orig_time_offset
         absolute_offset = absolute_onset + self.duration
 
         tmin = tmin if tmin is not None else absolute_onset.min()
@@ -360,14 +360,14 @@ def crop(self, tmin=None, tmax=None, emit_warning=False):
         if tmin > tmax:
             raise ValueError('tmax should be greater than tmin.')
 
-        if tmin < 0:
+        if tmin < orig_time_offset:
             raise ValueError('tmin should be positive.')
 
         out_of_bounds = (absolute_onset > tmax) | (absolute_offset < tmin)
 
         # clip the left side
         clip_left_elem = (absolute_onset < tmin) & ~out_of_bounds
-        self.onset[clip_left_elem] = tmin - offset
+        self.onset[clip_left_elem] = tmin - orig_time_offset
         diff = tmin - absolute_onset[clip_left_elem]
         self.duration[clip_left_elem] = self.duration[clip_left_elem] - diff
 


Test patch:
diff --git a/mne/tests/test_annotations.py b/mne/tests/test_annotations.py
--- a/mne/tests/test_annotations.py
+++ b/mne/tests/test_annotations.py
@@ -959,4 +959,16 @@ def test_date_none(tmpdir):
     assert raw_read.info['meas_date'] is None
 
 
+def test_negative_meas_dates():
+    """Test meas_date previous to 1970."""
+    # Regression test for gh-6621
+    raw = RawArray(data=np.empty((1, 1), dtype=np.float64),
+                   info=create_info(ch_names=1, sfreq=1.))
+    raw.info['meas_date'] = (-908196946, 988669)
+    raw.set_annotations(Annotations(description='foo', onset=[0],
+                                    duration=[0], orig_time=None))
+    events, _ = events_from_annotations(raw)
+    assert events[:, 0] == 0
+
+
 run_tests_if_main()