def cumulative_before_during_after(self,
                                       start: datetime.datetime,
                                       when: datetime.datetime) -> \
            Tuple[datetime.timedelta,
                  datetime.timedelta,
                  datetime.timedelta]:
        """
        For a given time, ``when``, returns the cumulative time
        
        - after ``start`` but before ``self`` begins, prior to ``when``;
        - after ``start`` and during intervals represented by ``self``, prior
          to ``when``;
        - after ``start`` and after at least one interval represented by
          ``self`` has finished, and not within any intervals represented by
          ``self``, and prior to ``when``.
          
        Args:
            start: the start time of interest (e.g. before ``self`` begins)
            when: the time of interest
            
        Returns:
            tuple: ``before, during, after``

        Illustration
        
        .. code-block:: none
        
        
            start:      S
            self:           X---X       X---X       X---X       X---X
            
            when:                                           W
            
            before:     ----
            during:         -----       -----       -----
            after:               -------     -------     ----

        """
        assert self.no_overlap, (
            "Only implemented for IntervalList objects with no_overlap == True"
        )
        no_time = datetime.timedelta()
        earliest_interval_start = self.start_datetime()

        # Easy special cases
        if when <= start:
            return no_time, no_time, no_time
        if self.is_empty() or when <= earliest_interval_start:
            return when - start, no_time, no_time

        # Now we can guarantee:
        # - "self" is a non-empty list
        # - start < when
        # - earliest_interval_start < when

        # Before
        if earliest_interval_start < start:
            before = no_time
        else:
            before = earliest_interval_start - start

        # During
        during = self.cumulative_time_to(when)

        after = (
            self.cumulative_gaps_to(when) +
            self.time_afterwards_preceding(when)
        )

        return before, during, after
For a given time, ``when``, returns the cumulative time
        
        - after ``start`` but before ``self`` begins, prior to ``when``;
        - after ``start`` and during intervals represented by ``self``, prior
          to ``when``;
        - after ``start`` and after at least one interval represented by
          ``self`` has finished, and not within any intervals represented by
          ``self``, and prior to ``when``.
          
        Args:
            start: the start time of interest (e.g. before ``self`` begins)
            when: the time of interest
            
        Returns:
            tuple: ``before, during, after``

        Illustration
        
        .. code-block:: none
        
        
            start:      S
            self:           X---X       X---X       X---X       X---X
            
            when:                                           W
            
            before:     ----
            during:         -----       -----       -----
            after:               -------     -------     ----