ES.timeslot

Created on Sun Jan 2 18:30:14 2022

@author: Philippe@loco-labs.io

The ES.timeslot module contains the TimeSlot and the TimeInterval classes.

What is the TimeSlot Object ?

The TimeSlot Object is a representation of time intervals data and properties. For example, I can represent the working day of 2022-march-15 by a TimeSlot which includes the following intervals:

  • from 9 a.m. to 12 p.m.
  • from 2 p.m. to 4:30 p.m.
  • from 5 p.m. to 7:30 p.m. i.e. a duration of 8 hours centered around 3 p.m. with bounds at 9 a.m. and 7:30 p.m.

Main principles

The main principles are as follows :

Data structure

A TimeSlot is a list of TimeInterval.

A TimeInterval is defined by two datetime objects (start and end)

Multiple properties are associated with the data :

  • duration : sum of the lenght of each TimeInterval
  • centroïd : instant assicited to the middle of the duration
  • bounds : minimum, maximum and middle
  • type : instant, interval or slot

Relationships and assembly

Two TimeSlot can be compared with five statuses (equals, contains, whitin, disjoint, intersects).

Multiple operations between two objects can be performed :

  1# -*- coding: utf-8 -*-
  2"""
  3Created on Sun Jan  2 18:30:14 2022
  4
  5@author: Philippe@loco-labs.io
  6
  7The `ES.timeslot` module contains the `TimeSlot` and the `TimeInterval` classes.
  8
  9# What is the TimeSlot Object ?
 10
 11The TimeSlot Object is a representation of time intervals data and properties. For example,
 12 I can represent the working day of 2022-march-15 by a TimeSlot which includes the following intervals:
 13- from 9 a.m. to 12 p.m.
 14- from 2 p.m. to 4:30 p.m.
 15- from 5 p.m. to 7:30 p.m.
 16i.e. a duration of 8 hours centered around 3 p.m. with bounds at 9 a.m. and 7:30 p.m.
 17
 18# Main principles
 19
 20The main principles are as follows :
 21
 22<img src="./timeslot_data_structure.png" width="800">
 23    
 24## Data structure
 25
 26A `TimeSlot` is a list of `TimeInterval`.
 27
 28A `TimeInterval` is defined by two `datetime` objects (start and end)
 29
 30Multiple properties are associated with the data :
 31    
 32- duration : sum of the lenght of each TimeInterval
 33- centroïd : instant assicited to the middle of the duration
 34- bounds : minimum, maximum and middle
 35- type : instant, interval or slot
 36
 37## Relationships and assembly
 38
 39Two `TimeSlot` can be compared with five statuses (equals, contains, whitin, disjoint, intersects).
 40
 41Multiple operations between two objects can be performed :
 42    
 43- union between two `TimeSlot`
 44- intersection between two `TimeSlot`
 45- complementing a `TimeSlot` in an interval
 46
 47"""
 48import datetime
 49import json, numpy, pandas, bson
 50from ESconstante import ES #, _identity
 51
 52class TimeSlotEncoder(json.JSONEncoder):
 53    """add a new json encoder for TimeSlot"""
 54    def default(self, o) :
 55        if isinstance(o, datetime.datetime) : return o.isoformat()
 56        return json.JSONEncoder.default(self, o)
 57
 58class TimeSlot:
 59    '''        
 60    *Attributes (for @property see methods)* :
 61
 62    - **slot** : list of `TimeInterval`
 63
 64    The methods defined in this class are : 
 65    
 66    *dynamic value property (getters)*
 67    
 68    - `TimeSlot.Bounds`
 69    - `TimeSlot.bounds`
 70    - `TimeSlot.Centroid`
 71    - `TimeSlot.duration`
 72    - `TimeSlot.instant`
 73    - `TimeSlot.middle`
 74    - `TimeSlot.interval`
 75    - `TimeSlot.stype`
 76     
 77    *instance methods*
 78
 79    - `TimeSlot.json`
 80    - `TimeSlot.link`
 81    - `TimeSlot.timetuple`
 82    - `TimeSlot.union`
 83    '''
 84    def __init__(self, val=None):
 85        '''
 86        TimeSlot constructor.
 87
 88        *Parameters*
 89        
 90        - **val** : date, interval, list of interval (default None) - with several formats 
 91        (tuple, list, string, datetime, TimeSlot, TimeInterval, numpy datetime64, pandas timestamp)
 92        
 93        *Returns* : None'''          
 94        slot = []
 95        if isinstance(val, str):
 96            try:        val = json.loads(val)   
 97            except:
 98                val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
 99                #try:    val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
100                #except: val = None    
101        if val == None : 
102            self.slot = slot
103            return
104        if isinstance(val, tuple): val = list(val)
105        if isinstance(val, list) and len(val) == 2 and not isinstance(val[0], TimeInterval):  
106            try :                           slot.append(TimeInterval(val))
107            except :
108                for interv in val :         slot.append(TimeInterval(interv))
109        elif isinstance(val, list):  
110            try :
111                for interv in val :         slot.append(TimeInterval(interv))
112            except :                        slot.append(TimeInterval(val))
113        elif isinstance(val, datetime.datetime): slot.append(TimeInterval(val))
114        elif isinstance(val, TimeSlot):     slot = val.slot
115        elif isinstance(val, TimeInterval): slot.append(val)
116        else :                              slot.append(TimeInterval(val))
117        self.slot= TimeSlot._reduced(slot)
118        
119    def __add__(self, other):
120        ''' Add other's values to self's values in a new TimeSlot'''
121        return TimeSlot(TimeSlot._reduced(self.slot + other.slot))
122
123    def __iadd__(self, other):
124        ''' Add other's values to self's values'''
125        self.slot = self._reduced(self.slot + other.slot)
126        
127    def __contains__(self, item):
128        ''' item of extval'''
129        return item in self.slot
130
131    def __getitem__(self, index):
132        ''' return interval item'''
133        return self.slot[index]
134
135    def __setitem__(self, index, interv): 
136        ''' modify interval item'''
137        if index < 0 or index >= len(self) : raise TimeSlotError("out of bounds")
138        self.slot[index] = TimeInterval(interv)
139        self.slot= TimeSlot._reduced(self.slot)
140        
141    def __len__(self): 
142        '''return the number of intervals included'''
143        return len(self.slot)
144    
145    #def __repr__(self):
146    def __str__(self):
147        ''' return the type of slot and the json representation'''
148        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
149
150    def __repr__(self):
151        #return self.__class__.__name__ + f'({self.slot})'
152        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
153        
154    def __eq__(self, other):
155        '''equal if the slots are equals'''
156        try: return self.slot == other.slot
157        except: return False
158               
159    def __lt__(self, other): 
160        '''compare the earliest dates'''
161        return self.slot[0] < other.slot[0]
162
163    def __hash__(self): 
164        return sum(hash(interv) for interv in self.slot)
165        #return hash(self.json(True))
166 
167    @property
168    def Bounds(self):
169        '''return an interval TimeSlot with the bounds of the TimeSlot object'''
170        return TimeSlot(self.bounds)
171    
172    @property
173    def bounds(self): 
174        '''return a tuple with the start and end dates with isoformat string'''
175        return (TimeSlot.form(self.slot[0].start), TimeSlot.form(self.slot[len(self) - 1].end))
176
177    @classmethod
178    def cast(cls, value):
179        '''
180        tranform a value (unique or list) in a list of `TimeSlot`
181
182        *Parameters*
183
184        - **value** : value to transform
185
186        *Returns*
187
188        - **list** : list of `TimeSlot`
189        '''
190        if isinstance(value, list):
191            try :
192                return [cls(val) for val in value]
193            except :
194                return [cls(value)]
195        else : return  [cls(value)]
196
197    @property
198    def Centroid(self):
199        '''return a TimeSlot with the date corresponding to the middle of the duration'''
200        return TimeSlot(self.instant)
201    
202    @property
203    def duration(self):
204        '''cumulative duration of each interval (timedelta format)'''
205        duration = datetime.timedelta()
206        for interv in self.slot : duration += interv.duration
207        return duration
208    
209    @staticmethod 
210    def form(dtime):
211        if dtime.timetuple()[3:6]==(0,0,0): return dtime.date().isoformat()
212        return dtime.isoformat()
213    
214    @property
215    def instant(self): 
216        '''return the date corresponding to the middle of the duration (datetime format)'''
217        duration = self.duration / 2
218        for interv in self.slot :
219            if duration > interv.duration : 
220                duration -= interv.duration
221            else :
222                return interv.start + duration
223    
224    @property
225    def middle(self): 
226        '''return the date corresponding to the middle of the bounds (datetime format)'''
227        return self.bounds.instant
228    
229    @property    
230    def name(self):
231        ''' class name'''
232        return self.__class__.__name__
233
234    @property
235    def interval(self): 
236        '''return a list with the start and end dates (datetime format)'''
237        return [self.slot[0].start, self.slot[len(self) - 1].end]
238    
239    @property
240    def stype(self):
241        '''return a string with the type of TimeSlot (instant, interval, slot)'''
242        if len(self.slot) == 1 : return self.slot[0].stype
243        else : return 'slot'
244
245    def json(self, **kwargs): 
246        '''
247        Return json/bson structure with the list of TimeInterval.
248
249        *Parameters*
250        
251        - **encoded** : defaut False - if False return dict, else return json string/bson bytes
252        - **encode_format** : defaut 'json' - return json, bson or cbor format
253                
254        *Returns* : string or dict'''
255        option = {'encoded' : False, 'encode_format' : 'json'} | kwargs
256        if len(self) == 1 : js = self.slot[0].json(encoded=False, encode_format=option['encode_format'])
257        else : js = [interv.json(encoded=False, encode_format=option['encode_format']) for interv in self.slot]
258        if option['encoded'] and option['encode_format'] == 'json': return json.dumps(js, cls=TimeSlotEncoder)
259        if option['encoded'] and option['encode_format'] == 'bson': return bson.encode(js)
260        return js
261
262    def link(self, other):
263        '''
264        Return the status (string) of the link between two TimeSlot (self and other).
265        - equals     : if self and other are the same
266        - disjoint   : if self's intervals and other's intervals are all disjoint
267        - within     : if all self's intervals are included in other's intervals
268        - contains   : if all other's intervals are included in self's intervals
269        - intersects : in the others cases
270
271        *Parameters*
272        
273        - **other** : TimeSlot to be compared
274                
275        *Returns* 
276        
277        - **tuple** : (string(status), boolean(full or not))'''
278        if self.stype == 'instant' : point, oslot = self[0], other
279        elif other.stype == 'instant' : point, oslot = other[0], self
280        else : point = None
281        if point is not None :
282            contains = equals = False
283            for interv in oslot:
284                contains = contains or interv.link(point) == 'contains'
285                equals = equals or interv.link(point) == 'equals'
286            if equals and not contains : return ('equals', True)
287            if contains and point == other[0] : return ('contains', True) 
288            if contains and point == self[0] : return ('within', True)
289            return ('disjoint', True)
290        else :
291            union = self + other
292            link = 'intersects'
293            full = True
294            if   union.duration == self.duration == other.duration : 
295                full = len(union) == len(self) == len(other)
296                link = 'equals'
297            elif union.duration == self.duration :
298                full = len(union) == len(self)
299                link = 'contains'
300            elif union.duration == other.duration :
301                full = len(union) == len(other)
302                link = 'within'
303            elif union.duration == self.duration + other.duration : 
304                full = len(union) == len(self) + len(other)
305                link = 'disjoint'
306            return (link, full)
307
308    def timetuple(self, index=0, encoded=False): 
309        '''
310        Return json structure with the list of TimeInterval (timetuple filter).
311
312        *Parameters*
313        
314        - **index** : integer, defaut 0 - timetuple format to apply :
315            - 0 : year
316            - 1 : month
317            - 2 : day
318            - 3 : hour
319            - 4 : minute
320            - 5 : seconds
321            - 6 : weekday
322            - 7 : yearday
323            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
324        - **encoded** : defaut False - if True return string, else return dict
325                
326        *Returns* : string or dict'''
327        if len(self) == 1 : js = self.slot[0].timetuple(index, False)
328        else : js = [interv.timetuple(index, False) for interv in self.slot]
329        if encoded : return json.dumps(js, cls=TimeSlotEncoder)
330        else : return js
331    
332    def union(self, other):
333        ''' Add other's values to self's values in a new TimeSlot (same as __add__)'''
334        return self.__add__(other)
335
336    @staticmethod    
337    def _reduced(listinterv):
338        ''' return an ordered and non-overlapping list of TimeInterval from any TimeInterval list'''
339        if not isinstance(listinterv, list) or len(listinterv) == 0 : return []
340        union = []
341        slot = sorted(listinterv)
342        interv = slot[0]
343        i = j = 0
344        while i < len(slot) :
345            for j in range(i + 1, len(slot)):
346                if   interv.link(slot[j]) == 'within'      : interv = slot[j]
347                elif interv.link(slot[j]) == 'intersects'  : interv = interv.union(slot[j])
348                elif interv.link(slot[j]) == 'disjoint' :
349                    union.append(interv)
350                    interv = slot[j]
351                    i = j
352                    break
353            if j >= len(slot) - 1 : 
354                union.append(interv)
355                break
356        return union
357    
358class TimeInterval:    # !!! interval
359    '''        
360    *Attributes (for @property see methods)* :
361
362    - **start** : datetime Object - start of `TimeInterval`
363    - **end**   : datetime Object - end of `TimeInterval`
364
365    The methods defined in this class are : 
366    
367    *dynamic value property (getters)*
368    
369    - `TimeInterval.Bounds`
370    - `TimeInterval.bounds`
371    - `TimeInterval.Centroid`
372    - `TimeInterval.duration`
373    - `TimeInterval.instant`
374    - `TimeInterval.stype`
375     
376    *instance methods*
377
378    - `TimeInterval.json`
379    - `TimeInterval.link`
380    - `TimeInterval.timetuple`
381    - `TimeInterval.union`
382    '''    
383    def __init__(self, val= ES.nullDate):
384        '''
385        TimeInterval constructor.
386
387        *Parameters*
388        
389        - **val** : date, interval (default ES.nullDate) - with several formats 
390        (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)
391        
392        *Returns* : None'''          
393        self.start = self.end = ES.nullDate
394        if isinstance(val, str):
395            try:
396                sl = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
397                if sl != None : self.start = self.end = sl
398                return
399            except:
400                try:     val = json.loads(val)
401                except:  val = ES.nullDate    
402        if   isinstance(val, list) : self._initInterval(val)
403        elif isinstance(val, TimeInterval) :  self.start, self.end = val.start, val.end
404        else : 
405            dat = self._initDat(val)
406            if dat != None : self.start = self.end = dat
407
408    #def __repr__(self):
409    def __str__(self):
410        ''' return the type of interval and the json representation'''
411        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
412    
413    def __repr__(self):
414        #if self.stype == 'instant' : return self.__class__.__name__ + f'("{self.start}")'
415        #return self.__class__.__name__ + f'(["{self.start}","{self.end}"])'
416        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
417
418    def __eq__(self, other):
419        '''equal if the 'start' and 'end' dates are equals'''
420        return self.start == other.start and self.end == other.end
421
422    def __lt__(self, other): 
423        '''compare the earliest dates (start)'''
424        return self.start < other.start
425
426    def __hash__(self): 
427        return hash(self.start) + hash(self.end)
428        #return hash(self.json(True))
429        
430    @property
431    def bounds(self): 
432        '''return a tuple with the start and end dates with isoformat string'''
433        return (TimeSlot.form(self.start), TimeSlot.form(self.end))
434        
435    @property
436    def Centroid(self):
437        '''return a TimeInterval with the date corresponding to the middle of the interval'''
438        return TimeInterval(self.instant)
439    
440    @property
441    def duration(self):
442        '''duration between 'end' and 'start' date (timedelta format)'''
443        return self.end - self.start
444    
445    @property
446    def instant(self): 
447        '''return the date corresponding to the middle of the duration (datetime format)'''
448        return self.start + (self.end - self.start) / 2
449
450    @property
451    def stype(self):
452        '''return a string with the type of TimeInterval (instant, interval)'''
453        if self.start == self.end : return 'instant'
454        return 'interval'
455
456    def json(self, encoded=False, encode_format='json'): 
457        '''
458        Return json/bson structure (date if 'instant' or [start, end] if 'interval') 
459        with datetime or datetime.isoformat for dates.
460
461        *Parameters*
462        
463        - **encoded** : defaut False - if True return dict, else return json string/bson bytes
464        - **encode_format**   : defaut 'json' - return json, bson or cbor format
465                
466        *Returns* : string or dict'''
467        if  self.stype == 'instant':    js = self.start
468        else:                           js = [self.start, self.end]
469        '''if   self.stype == 'instant' : 
470            if encode_format == 'bson':  js = self.start
471            else:           
472                js = TimeSlot.form(self.start)
473        elif self.stype == 'interval' : 
474            if encode_format == 'bson':  js = [self.start, self.end]
475            else:           js = [TimeSlot.form(self.start), TimeSlot.form(self.end)]'''
476        if encoded and encode_format == 'json': return json.dumps(js, cls=TimeSlotEncoder)
477        if encoded and encode_format == 'bson': return bson.encode(js)
478        return js
479    
480    def link(self, other):
481        '''
482        Return the status (string) of the link between two TimeIntervals (self and other).
483        - equals     : if self and other are the same
484        - disjoint   : if self's interval and other's interval are disjoint
485        - within     : if other's interval is included in self's interval
486        - contains   : if self's interval is included in other's interval
487        - intersects : in the others cases
488
489        *Parameters*
490        
491        - **other** : TimeInterval to be compared
492                
493        *Returns* : string'''
494        if self.start == other.start and self.end == other.end : return 'equals'
495        if self.start <= other.start and self.end >= other.end : return 'contains'
496        if self.start >= other.start and self.end <= other.end : return 'within'
497        if self.start <= other.end and self.end >= other.start : return 'intersects'
498        return 'disjoint'
499
500    def timetuple(self, index=0, encoded=False): 
501        '''
502        Return json structure (timetuple filter).
503
504        *Parameters*
505        
506        - **index** : integer, defaut 0 - timetuple format to apply :
507            - 0 : year
508            - 1 : month
509            - 2 : day
510            - 3 : hour
511            - 4 : minute
512            - 5 : seconds
513            - 6 : weekday
514            - 7 : yearday
515            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
516        - **encoded** : defaut False - if True return string, else return dict
517                
518        *Returns* : string or dict'''
519        if index not in [0,1,2,3,4,5,6,7,8] : return None
520        if self.stype == 'instant' : js = self.start.timetuple()[index]
521        elif self.stype == 'interval' : js = [self.start.timetuple()[index], self.end.timetuple()[index]]
522        if encoded : return json.dumps(js, cls=TimeSlotEncoder)
523        else : return js
524
525    def union(self, other):
526        ''' Add other's values to self's values in a new TimeInterval 
527        if self and other are not disjoint'''
528        if self.link(other) != 'disjoint' : return TimeInterval([min(self.start, other.start), max(self.end, other.end)])
529        else : return None
530
531    def _initInterval(self, val):
532        '''initialization of start and end dates from a list'''
533        self.start = self.end = self._initDat(val[0])
534        if len(val) > 1 : self.end = self._initDat(val[1])
535        else :    self.start = self.end = self._initDat(val)
536        if self.end < self.start : self.start, self.end = self.end, self.start
537
538    def _initDat(self, val):
539        '''initialization of start and end dates from a unique value 
540        (datetime, string, numpy.datetime64, pandas Timestamp)'''
541        if   isinstance(val, datetime.datetime):
542            res = val
543            '''if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
544                res = val.astimezone(datetime.timezone.utc)
545            else: res = val'''
546        elif isinstance(val, str):
547            try : res = datetime.datetime.fromisoformat(val)
548            except: res = ES.nullDate
549        elif isinstance(val, numpy.datetime64):
550            res = pandas.Timestamp(val).to_pydatetime()
551        elif isinstance(val, pandas._libs.tslibs.timestamps.Timestamp):
552            res = val.to_pydatetime()
553        else : raise TimeSlotError("impossible to convert in a date")
554        return TimeInterval._dattz(res)
555
556    @staticmethod 
557    def _dattz(val):
558        if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
559            return val.replace(tzinfo=datetime.timezone.utc)
560        return val         
561        
562class TimeSlotError(Exception):
563    pass
564    
class TimeSlotEncoder(json.encoder.JSONEncoder):
53class TimeSlotEncoder(json.JSONEncoder):
54    """add a new json encoder for TimeSlot"""
55    def default(self, o) :
56        if isinstance(o, datetime.datetime) : return o.isoformat()
57        return json.JSONEncoder.default(self, o)

add a new json encoder for TimeSlot

def default(self, o):
55    def default(self, o) :
56        if isinstance(o, datetime.datetime) : return o.isoformat()
57        return json.JSONEncoder.default(self, o)

Implement this method in a subclass such that it returns a serializable object for o, or calls the base implementation (to raise a TypeError).

For example, to support arbitrary iterators, you could implement default like this::

def default(self, o):
    try:
        iterable = iter(o)
    except TypeError:
        pass
    else:
        return list(iterable)
    # Let the base class default method raise the TypeError
    return JSONEncoder.default(self, o)
Inherited Members
json.encoder.JSONEncoder
JSONEncoder
encode
iterencode
class TimeSlot:
 59class TimeSlot:
 60    '''        
 61    *Attributes (for @property see methods)* :
 62
 63    - **slot** : list of `TimeInterval`
 64
 65    The methods defined in this class are : 
 66    
 67    *dynamic value property (getters)*
 68    
 69    - `TimeSlot.Bounds`
 70    - `TimeSlot.bounds`
 71    - `TimeSlot.Centroid`
 72    - `TimeSlot.duration`
 73    - `TimeSlot.instant`
 74    - `TimeSlot.middle`
 75    - `TimeSlot.interval`
 76    - `TimeSlot.stype`
 77     
 78    *instance methods*
 79
 80    - `TimeSlot.json`
 81    - `TimeSlot.link`
 82    - `TimeSlot.timetuple`
 83    - `TimeSlot.union`
 84    '''
 85    def __init__(self, val=None):
 86        '''
 87        TimeSlot constructor.
 88
 89        *Parameters*
 90        
 91        - **val** : date, interval, list of interval (default None) - with several formats 
 92        (tuple, list, string, datetime, TimeSlot, TimeInterval, numpy datetime64, pandas timestamp)
 93        
 94        *Returns* : None'''          
 95        slot = []
 96        if isinstance(val, str):
 97            try:        val = json.loads(val)   
 98            except:
 99                val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
100                #try:    val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
101                #except: val = None    
102        if val == None : 
103            self.slot = slot
104            return
105        if isinstance(val, tuple): val = list(val)
106        if isinstance(val, list) and len(val) == 2 and not isinstance(val[0], TimeInterval):  
107            try :                           slot.append(TimeInterval(val))
108            except :
109                for interv in val :         slot.append(TimeInterval(interv))
110        elif isinstance(val, list):  
111            try :
112                for interv in val :         slot.append(TimeInterval(interv))
113            except :                        slot.append(TimeInterval(val))
114        elif isinstance(val, datetime.datetime): slot.append(TimeInterval(val))
115        elif isinstance(val, TimeSlot):     slot = val.slot
116        elif isinstance(val, TimeInterval): slot.append(val)
117        else :                              slot.append(TimeInterval(val))
118        self.slot= TimeSlot._reduced(slot)
119        
120    def __add__(self, other):
121        ''' Add other's values to self's values in a new TimeSlot'''
122        return TimeSlot(TimeSlot._reduced(self.slot + other.slot))
123
124    def __iadd__(self, other):
125        ''' Add other's values to self's values'''
126        self.slot = self._reduced(self.slot + other.slot)
127        
128    def __contains__(self, item):
129        ''' item of extval'''
130        return item in self.slot
131
132    def __getitem__(self, index):
133        ''' return interval item'''
134        return self.slot[index]
135
136    def __setitem__(self, index, interv): 
137        ''' modify interval item'''
138        if index < 0 or index >= len(self) : raise TimeSlotError("out of bounds")
139        self.slot[index] = TimeInterval(interv)
140        self.slot= TimeSlot._reduced(self.slot)
141        
142    def __len__(self): 
143        '''return the number of intervals included'''
144        return len(self.slot)
145    
146    #def __repr__(self):
147    def __str__(self):
148        ''' return the type of slot and the json representation'''
149        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
150
151    def __repr__(self):
152        #return self.__class__.__name__ + f'({self.slot})'
153        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
154        
155    def __eq__(self, other):
156        '''equal if the slots are equals'''
157        try: return self.slot == other.slot
158        except: return False
159               
160    def __lt__(self, other): 
161        '''compare the earliest dates'''
162        return self.slot[0] < other.slot[0]
163
164    def __hash__(self): 
165        return sum(hash(interv) for interv in self.slot)
166        #return hash(self.json(True))
167 
168    @property
169    def Bounds(self):
170        '''return an interval TimeSlot with the bounds of the TimeSlot object'''
171        return TimeSlot(self.bounds)
172    
173    @property
174    def bounds(self): 
175        '''return a tuple with the start and end dates with isoformat string'''
176        return (TimeSlot.form(self.slot[0].start), TimeSlot.form(self.slot[len(self) - 1].end))
177
178    @classmethod
179    def cast(cls, value):
180        '''
181        tranform a value (unique or list) in a list of `TimeSlot`
182
183        *Parameters*
184
185        - **value** : value to transform
186
187        *Returns*
188
189        - **list** : list of `TimeSlot`
190        '''
191        if isinstance(value, list):
192            try :
193                return [cls(val) for val in value]
194            except :
195                return [cls(value)]
196        else : return  [cls(value)]
197
198    @property
199    def Centroid(self):
200        '''return a TimeSlot with the date corresponding to the middle of the duration'''
201        return TimeSlot(self.instant)
202    
203    @property
204    def duration(self):
205        '''cumulative duration of each interval (timedelta format)'''
206        duration = datetime.timedelta()
207        for interv in self.slot : duration += interv.duration
208        return duration
209    
210    @staticmethod 
211    def form(dtime):
212        if dtime.timetuple()[3:6]==(0,0,0): return dtime.date().isoformat()
213        return dtime.isoformat()
214    
215    @property
216    def instant(self): 
217        '''return the date corresponding to the middle of the duration (datetime format)'''
218        duration = self.duration / 2
219        for interv in self.slot :
220            if duration > interv.duration : 
221                duration -= interv.duration
222            else :
223                return interv.start + duration
224    
225    @property
226    def middle(self): 
227        '''return the date corresponding to the middle of the bounds (datetime format)'''
228        return self.bounds.instant
229    
230    @property    
231    def name(self):
232        ''' class name'''
233        return self.__class__.__name__
234
235    @property
236    def interval(self): 
237        '''return a list with the start and end dates (datetime format)'''
238        return [self.slot[0].start, self.slot[len(self) - 1].end]
239    
240    @property
241    def stype(self):
242        '''return a string with the type of TimeSlot (instant, interval, slot)'''
243        if len(self.slot) == 1 : return self.slot[0].stype
244        else : return 'slot'
245
246    def json(self, **kwargs): 
247        '''
248        Return json/bson structure with the list of TimeInterval.
249
250        *Parameters*
251        
252        - **encoded** : defaut False - if False return dict, else return json string/bson bytes
253        - **encode_format** : defaut 'json' - return json, bson or cbor format
254                
255        *Returns* : string or dict'''
256        option = {'encoded' : False, 'encode_format' : 'json'} | kwargs
257        if len(self) == 1 : js = self.slot[0].json(encoded=False, encode_format=option['encode_format'])
258        else : js = [interv.json(encoded=False, encode_format=option['encode_format']) for interv in self.slot]
259        if option['encoded'] and option['encode_format'] == 'json': return json.dumps(js, cls=TimeSlotEncoder)
260        if option['encoded'] and option['encode_format'] == 'bson': return bson.encode(js)
261        return js
262
263    def link(self, other):
264        '''
265        Return the status (string) of the link between two TimeSlot (self and other).
266        - equals     : if self and other are the same
267        - disjoint   : if self's intervals and other's intervals are all disjoint
268        - within     : if all self's intervals are included in other's intervals
269        - contains   : if all other's intervals are included in self's intervals
270        - intersects : in the others cases
271
272        *Parameters*
273        
274        - **other** : TimeSlot to be compared
275                
276        *Returns* 
277        
278        - **tuple** : (string(status), boolean(full or not))'''
279        if self.stype == 'instant' : point, oslot = self[0], other
280        elif other.stype == 'instant' : point, oslot = other[0], self
281        else : point = None
282        if point is not None :
283            contains = equals = False
284            for interv in oslot:
285                contains = contains or interv.link(point) == 'contains'
286                equals = equals or interv.link(point) == 'equals'
287            if equals and not contains : return ('equals', True)
288            if contains and point == other[0] : return ('contains', True) 
289            if contains and point == self[0] : return ('within', True)
290            return ('disjoint', True)
291        else :
292            union = self + other
293            link = 'intersects'
294            full = True
295            if   union.duration == self.duration == other.duration : 
296                full = len(union) == len(self) == len(other)
297                link = 'equals'
298            elif union.duration == self.duration :
299                full = len(union) == len(self)
300                link = 'contains'
301            elif union.duration == other.duration :
302                full = len(union) == len(other)
303                link = 'within'
304            elif union.duration == self.duration + other.duration : 
305                full = len(union) == len(self) + len(other)
306                link = 'disjoint'
307            return (link, full)
308
309    def timetuple(self, index=0, encoded=False): 
310        '''
311        Return json structure with the list of TimeInterval (timetuple filter).
312
313        *Parameters*
314        
315        - **index** : integer, defaut 0 - timetuple format to apply :
316            - 0 : year
317            - 1 : month
318            - 2 : day
319            - 3 : hour
320            - 4 : minute
321            - 5 : seconds
322            - 6 : weekday
323            - 7 : yearday
324            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
325        - **encoded** : defaut False - if True return string, else return dict
326                
327        *Returns* : string or dict'''
328        if len(self) == 1 : js = self.slot[0].timetuple(index, False)
329        else : js = [interv.timetuple(index, False) for interv in self.slot]
330        if encoded : return json.dumps(js, cls=TimeSlotEncoder)
331        else : return js
332    
333    def union(self, other):
334        ''' Add other's values to self's values in a new TimeSlot (same as __add__)'''
335        return self.__add__(other)
336
337    @staticmethod    
338    def _reduced(listinterv):
339        ''' return an ordered and non-overlapping list of TimeInterval from any TimeInterval list'''
340        if not isinstance(listinterv, list) or len(listinterv) == 0 : return []
341        union = []
342        slot = sorted(listinterv)
343        interv = slot[0]
344        i = j = 0
345        while i < len(slot) :
346            for j in range(i + 1, len(slot)):
347                if   interv.link(slot[j]) == 'within'      : interv = slot[j]
348                elif interv.link(slot[j]) == 'intersects'  : interv = interv.union(slot[j])
349                elif interv.link(slot[j]) == 'disjoint' :
350                    union.append(interv)
351                    interv = slot[j]
352                    i = j
353                    break
354            if j >= len(slot) - 1 : 
355                union.append(interv)
356                break
357        return union

Attributes (for @property see methods) :

The methods defined in this class are :

dynamic value property (getters)

instance methods

TimeSlot(val=None)
 85    def __init__(self, val=None):
 86        '''
 87        TimeSlot constructor.
 88
 89        *Parameters*
 90        
 91        - **val** : date, interval, list of interval (default None) - with several formats 
 92        (tuple, list, string, datetime, TimeSlot, TimeInterval, numpy datetime64, pandas timestamp)
 93        
 94        *Returns* : None'''          
 95        slot = []
 96        if isinstance(val, str):
 97            try:        val = json.loads(val)   
 98            except:
 99                val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
100                #try:    val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
101                #except: val = None    
102        if val == None : 
103            self.slot = slot
104            return
105        if isinstance(val, tuple): val = list(val)
106        if isinstance(val, list) and len(val) == 2 and not isinstance(val[0], TimeInterval):  
107            try :                           slot.append(TimeInterval(val))
108            except :
109                for interv in val :         slot.append(TimeInterval(interv))
110        elif isinstance(val, list):  
111            try :
112                for interv in val :         slot.append(TimeInterval(interv))
113            except :                        slot.append(TimeInterval(val))
114        elif isinstance(val, datetime.datetime): slot.append(TimeInterval(val))
115        elif isinstance(val, TimeSlot):     slot = val.slot
116        elif isinstance(val, TimeInterval): slot.append(val)
117        else :                              slot.append(TimeInterval(val))
118        self.slot= TimeSlot._reduced(slot)

TimeSlot constructor.

Parameters

  • val : date, interval, list of interval (default None) - with several formats (tuple, list, string, datetime, TimeSlot, TimeInterval, numpy datetime64, pandas timestamp)

Returns : None

Bounds

return an interval TimeSlot with the bounds of the TimeSlot object

bounds

return a tuple with the start and end dates with isoformat string

@classmethod
def cast(cls, value):
178    @classmethod
179    def cast(cls, value):
180        '''
181        tranform a value (unique or list) in a list of `TimeSlot`
182
183        *Parameters*
184
185        - **value** : value to transform
186
187        *Returns*
188
189        - **list** : list of `TimeSlot`
190        '''
191        if isinstance(value, list):
192            try :
193                return [cls(val) for val in value]
194            except :
195                return [cls(value)]
196        else : return  [cls(value)]

tranform a value (unique or list) in a list of TimeSlot

Parameters

  • value : value to transform

Returns

Centroid

return a TimeSlot with the date corresponding to the middle of the duration

duration

cumulative duration of each interval (timedelta format)

@staticmethod
def form(dtime):
210    @staticmethod 
211    def form(dtime):
212        if dtime.timetuple()[3:6]==(0,0,0): return dtime.date().isoformat()
213        return dtime.isoformat()
instant

return the date corresponding to the middle of the duration (datetime format)

middle

return the date corresponding to the middle of the bounds (datetime format)

name

class name

interval

return a list with the start and end dates (datetime format)

stype

return a string with the type of TimeSlot (instant, interval, slot)

def json(self, **kwargs):
246    def json(self, **kwargs): 
247        '''
248        Return json/bson structure with the list of TimeInterval.
249
250        *Parameters*
251        
252        - **encoded** : defaut False - if False return dict, else return json string/bson bytes
253        - **encode_format** : defaut 'json' - return json, bson or cbor format
254                
255        *Returns* : string or dict'''
256        option = {'encoded' : False, 'encode_format' : 'json'} | kwargs
257        if len(self) == 1 : js = self.slot[0].json(encoded=False, encode_format=option['encode_format'])
258        else : js = [interv.json(encoded=False, encode_format=option['encode_format']) for interv in self.slot]
259        if option['encoded'] and option['encode_format'] == 'json': return json.dumps(js, cls=TimeSlotEncoder)
260        if option['encoded'] and option['encode_format'] == 'bson': return bson.encode(js)
261        return js

Return json/bson structure with the list of TimeInterval.

Parameters

  • encoded : defaut False - if False return dict, else return json string/bson bytes
  • encode_format : defaut 'json' - return json, bson or cbor format

Returns : string or dict

def timetuple(self, index=0, encoded=False):
309    def timetuple(self, index=0, encoded=False): 
310        '''
311        Return json structure with the list of TimeInterval (timetuple filter).
312
313        *Parameters*
314        
315        - **index** : integer, defaut 0 - timetuple format to apply :
316            - 0 : year
317            - 1 : month
318            - 2 : day
319            - 3 : hour
320            - 4 : minute
321            - 5 : seconds
322            - 6 : weekday
323            - 7 : yearday
324            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
325        - **encoded** : defaut False - if True return string, else return dict
326                
327        *Returns* : string or dict'''
328        if len(self) == 1 : js = self.slot[0].timetuple(index, False)
329        else : js = [interv.timetuple(index, False) for interv in self.slot]
330        if encoded : return json.dumps(js, cls=TimeSlotEncoder)
331        else : return js

Return json structure with the list of TimeInterval (timetuple filter).

Parameters

  • index : integer, defaut 0 - timetuple format to apply :
    • 0 : year
    • 1 : month
    • 2 : day
    • 3 : hour
    • 4 : minute
    • 5 : seconds
    • 6 : weekday
    • 7 : yearday
    • 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
  • encoded : defaut False - if True return string, else return dict

Returns : string or dict

def union(self, other):
333    def union(self, other):
334        ''' Add other's values to self's values in a new TimeSlot (same as __add__)'''
335        return self.__add__(other)

Add other's values to self's values in a new TimeSlot (same as __add__)

class TimeInterval:
359class TimeInterval:    # !!! interval
360    '''        
361    *Attributes (for @property see methods)* :
362
363    - **start** : datetime Object - start of `TimeInterval`
364    - **end**   : datetime Object - end of `TimeInterval`
365
366    The methods defined in this class are : 
367    
368    *dynamic value property (getters)*
369    
370    - `TimeInterval.Bounds`
371    - `TimeInterval.bounds`
372    - `TimeInterval.Centroid`
373    - `TimeInterval.duration`
374    - `TimeInterval.instant`
375    - `TimeInterval.stype`
376     
377    *instance methods*
378
379    - `TimeInterval.json`
380    - `TimeInterval.link`
381    - `TimeInterval.timetuple`
382    - `TimeInterval.union`
383    '''    
384    def __init__(self, val= ES.nullDate):
385        '''
386        TimeInterval constructor.
387
388        *Parameters*
389        
390        - **val** : date, interval (default ES.nullDate) - with several formats 
391        (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)
392        
393        *Returns* : None'''          
394        self.start = self.end = ES.nullDate
395        if isinstance(val, str):
396            try:
397                sl = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
398                if sl != None : self.start = self.end = sl
399                return
400            except:
401                try:     val = json.loads(val)
402                except:  val = ES.nullDate    
403        if   isinstance(val, list) : self._initInterval(val)
404        elif isinstance(val, TimeInterval) :  self.start, self.end = val.start, val.end
405        else : 
406            dat = self._initDat(val)
407            if dat != None : self.start = self.end = dat
408
409    #def __repr__(self):
410    def __str__(self):
411        ''' return the type of interval and the json representation'''
412        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
413    
414    def __repr__(self):
415        #if self.stype == 'instant' : return self.__class__.__name__ + f'("{self.start}")'
416        #return self.__class__.__name__ + f'(["{self.start}","{self.end}"])'
417        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
418
419    def __eq__(self, other):
420        '''equal if the 'start' and 'end' dates are equals'''
421        return self.start == other.start and self.end == other.end
422
423    def __lt__(self, other): 
424        '''compare the earliest dates (start)'''
425        return self.start < other.start
426
427    def __hash__(self): 
428        return hash(self.start) + hash(self.end)
429        #return hash(self.json(True))
430        
431    @property
432    def bounds(self): 
433        '''return a tuple with the start and end dates with isoformat string'''
434        return (TimeSlot.form(self.start), TimeSlot.form(self.end))
435        
436    @property
437    def Centroid(self):
438        '''return a TimeInterval with the date corresponding to the middle of the interval'''
439        return TimeInterval(self.instant)
440    
441    @property
442    def duration(self):
443        '''duration between 'end' and 'start' date (timedelta format)'''
444        return self.end - self.start
445    
446    @property
447    def instant(self): 
448        '''return the date corresponding to the middle of the duration (datetime format)'''
449        return self.start + (self.end - self.start) / 2
450
451    @property
452    def stype(self):
453        '''return a string with the type of TimeInterval (instant, interval)'''
454        if self.start == self.end : return 'instant'
455        return 'interval'
456
457    def json(self, encoded=False, encode_format='json'): 
458        '''
459        Return json/bson structure (date if 'instant' or [start, end] if 'interval') 
460        with datetime or datetime.isoformat for dates.
461
462        *Parameters*
463        
464        - **encoded** : defaut False - if True return dict, else return json string/bson bytes
465        - **encode_format**   : defaut 'json' - return json, bson or cbor format
466                
467        *Returns* : string or dict'''
468        if  self.stype == 'instant':    js = self.start
469        else:                           js = [self.start, self.end]
470        '''if   self.stype == 'instant' : 
471            if encode_format == 'bson':  js = self.start
472            else:           
473                js = TimeSlot.form(self.start)
474        elif self.stype == 'interval' : 
475            if encode_format == 'bson':  js = [self.start, self.end]
476            else:           js = [TimeSlot.form(self.start), TimeSlot.form(self.end)]'''
477        if encoded and encode_format == 'json': return json.dumps(js, cls=TimeSlotEncoder)
478        if encoded and encode_format == 'bson': return bson.encode(js)
479        return js
480    
481    def link(self, other):
482        '''
483        Return the status (string) of the link between two TimeIntervals (self and other).
484        - equals     : if self and other are the same
485        - disjoint   : if self's interval and other's interval are disjoint
486        - within     : if other's interval is included in self's interval
487        - contains   : if self's interval is included in other's interval
488        - intersects : in the others cases
489
490        *Parameters*
491        
492        - **other** : TimeInterval to be compared
493                
494        *Returns* : string'''
495        if self.start == other.start and self.end == other.end : return 'equals'
496        if self.start <= other.start and self.end >= other.end : return 'contains'
497        if self.start >= other.start and self.end <= other.end : return 'within'
498        if self.start <= other.end and self.end >= other.start : return 'intersects'
499        return 'disjoint'
500
501    def timetuple(self, index=0, encoded=False): 
502        '''
503        Return json structure (timetuple filter).
504
505        *Parameters*
506        
507        - **index** : integer, defaut 0 - timetuple format to apply :
508            - 0 : year
509            - 1 : month
510            - 2 : day
511            - 3 : hour
512            - 4 : minute
513            - 5 : seconds
514            - 6 : weekday
515            - 7 : yearday
516            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
517        - **encoded** : defaut False - if True return string, else return dict
518                
519        *Returns* : string or dict'''
520        if index not in [0,1,2,3,4,5,6,7,8] : return None
521        if self.stype == 'instant' : js = self.start.timetuple()[index]
522        elif self.stype == 'interval' : js = [self.start.timetuple()[index], self.end.timetuple()[index]]
523        if encoded : return json.dumps(js, cls=TimeSlotEncoder)
524        else : return js
525
526    def union(self, other):
527        ''' Add other's values to self's values in a new TimeInterval 
528        if self and other are not disjoint'''
529        if self.link(other) != 'disjoint' : return TimeInterval([min(self.start, other.start), max(self.end, other.end)])
530        else : return None
531
532    def _initInterval(self, val):
533        '''initialization of start and end dates from a list'''
534        self.start = self.end = self._initDat(val[0])
535        if len(val) > 1 : self.end = self._initDat(val[1])
536        else :    self.start = self.end = self._initDat(val)
537        if self.end < self.start : self.start, self.end = self.end, self.start
538
539    def _initDat(self, val):
540        '''initialization of start and end dates from a unique value 
541        (datetime, string, numpy.datetime64, pandas Timestamp)'''
542        if   isinstance(val, datetime.datetime):
543            res = val
544            '''if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
545                res = val.astimezone(datetime.timezone.utc)
546            else: res = val'''
547        elif isinstance(val, str):
548            try : res = datetime.datetime.fromisoformat(val)
549            except: res = ES.nullDate
550        elif isinstance(val, numpy.datetime64):
551            res = pandas.Timestamp(val).to_pydatetime()
552        elif isinstance(val, pandas._libs.tslibs.timestamps.Timestamp):
553            res = val.to_pydatetime()
554        else : raise TimeSlotError("impossible to convert in a date")
555        return TimeInterval._dattz(res)
556
557    @staticmethod 
558    def _dattz(val):
559        if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
560            return val.replace(tzinfo=datetime.timezone.utc)
561        return val         

Attributes (for @property see methods) :

The methods defined in this class are :

dynamic value property (getters)

instance methods

TimeInterval(val=datetime.datetime(1, 1, 1, 0, 0, tzinfo=datetime.timezone.utc))
384    def __init__(self, val= ES.nullDate):
385        '''
386        TimeInterval constructor.
387
388        *Parameters*
389        
390        - **val** : date, interval (default ES.nullDate) - with several formats 
391        (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)
392        
393        *Returns* : None'''          
394        self.start = self.end = ES.nullDate
395        if isinstance(val, str):
396            try:
397                sl = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
398                if sl != None : self.start = self.end = sl
399                return
400            except:
401                try:     val = json.loads(val)
402                except:  val = ES.nullDate    
403        if   isinstance(val, list) : self._initInterval(val)
404        elif isinstance(val, TimeInterval) :  self.start, self.end = val.start, val.end
405        else : 
406            dat = self._initDat(val)
407            if dat != None : self.start = self.end = dat

TimeInterval constructor.

Parameters

  • val : date, interval (default ES.nullDate) - with several formats (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)

Returns : None

bounds

return a tuple with the start and end dates with isoformat string

Centroid

return a TimeInterval with the date corresponding to the middle of the interval

duration

duration between 'end' and 'start' date (timedelta format)

instant

return the date corresponding to the middle of the duration (datetime format)

stype

return a string with the type of TimeInterval (instant, interval)

def json(self, encoded=False, encode_format='json'):
457    def json(self, encoded=False, encode_format='json'): 
458        '''
459        Return json/bson structure (date if 'instant' or [start, end] if 'interval') 
460        with datetime or datetime.isoformat for dates.
461
462        *Parameters*
463        
464        - **encoded** : defaut False - if True return dict, else return json string/bson bytes
465        - **encode_format**   : defaut 'json' - return json, bson or cbor format
466                
467        *Returns* : string or dict'''
468        if  self.stype == 'instant':    js = self.start
469        else:                           js = [self.start, self.end]
470        '''if   self.stype == 'instant' : 
471            if encode_format == 'bson':  js = self.start
472            else:           
473                js = TimeSlot.form(self.start)
474        elif self.stype == 'interval' : 
475            if encode_format == 'bson':  js = [self.start, self.end]
476            else:           js = [TimeSlot.form(self.start), TimeSlot.form(self.end)]'''
477        if encoded and encode_format == 'json': return json.dumps(js, cls=TimeSlotEncoder)
478        if encoded and encode_format == 'bson': return bson.encode(js)
479        return js

Return json/bson structure (date if 'instant' or [start, end] if 'interval') with datetime or datetime.isoformat for dates.

Parameters

  • encoded : defaut False - if True return dict, else return json string/bson bytes
  • encode_format : defaut 'json' - return json, bson or cbor format

Returns : string or dict

def timetuple(self, index=0, encoded=False):
501    def timetuple(self, index=0, encoded=False): 
502        '''
503        Return json structure (timetuple filter).
504
505        *Parameters*
506        
507        - **index** : integer, defaut 0 - timetuple format to apply :
508            - 0 : year
509            - 1 : month
510            - 2 : day
511            - 3 : hour
512            - 4 : minute
513            - 5 : seconds
514            - 6 : weekday
515            - 7 : yearday
516            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
517        - **encoded** : defaut False - if True return string, else return dict
518                
519        *Returns* : string or dict'''
520        if index not in [0,1,2,3,4,5,6,7,8] : return None
521        if self.stype == 'instant' : js = self.start.timetuple()[index]
522        elif self.stype == 'interval' : js = [self.start.timetuple()[index], self.end.timetuple()[index]]
523        if encoded : return json.dumps(js, cls=TimeSlotEncoder)
524        else : return js

Return json structure (timetuple filter).

Parameters

  • index : integer, defaut 0 - timetuple format to apply :
    • 0 : year
    • 1 : month
    • 2 : day
    • 3 : hour
    • 4 : minute
    • 5 : seconds
    • 6 : weekday
    • 7 : yearday
    • 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
  • encoded : defaut False - if True return string, else return dict

Returns : string or dict

def union(self, other):
526    def union(self, other):
527        ''' Add other's values to self's values in a new TimeInterval 
528        if self and other are not disjoint'''
529        if self.link(other) != 'disjoint' : return TimeInterval([min(self.start, other.start), max(self.end, other.end)])
530        else : return None

Add other's values to self's values in a new TimeInterval if self and other are not disjoint

class TimeSlotError(builtins.Exception):
563class TimeSlotError(Exception):
564    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback