python.observation.timeslot

Created on Sun Jan 2 18:30:14 2022

@author: Philippe@loco-labs.io

The python.observation.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, within, 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 `python.observation.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, within, 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
 50import numpy
 51import pandas
 52import bson
 53
 54from observation.esconstante import ES  # , _identity
 55
 56
 57class TimeSlotEncoder(json.JSONEncoder):
 58    """add a new json encoder for TimeSlot"""
 59
 60    def default(self, o):
 61        if isinstance(o, datetime.datetime):
 62            return o.isoformat()
 63        return json.JSONEncoder.default(self, o)
 64
 65
 66class TimeSlot:
 67    '''        
 68    *Attributes (for @property see methods)* :
 69
 70    - **slot** : list of `TimeInterval`
 71
 72    The methods defined in this class are : 
 73
 74    *dynamic value property (getters)*
 75
 76    - `TimeSlot.Bounds`
 77    - `TimeSlot.bounds`
 78    - `TimeSlot.Centroid`
 79    - `TimeSlot.duration`
 80    - `TimeSlot.instant`
 81    - `TimeSlot.middle`
 82    - `TimeSlot.interval`
 83    - `TimeSlot.stype`
 84
 85    *instance methods*
 86
 87    - `TimeSlot.json`
 88    - `TimeSlot.link`
 89    - `TimeSlot.timetuple`
 90    - `TimeSlot.union`
 91    '''
 92
 93    def __init__(self, val=None):
 94        '''
 95        TimeSlot constructor.
 96
 97        *Parameters*
 98
 99        - **val** : date, interval, list of interval (default None) - with several formats 
100        (tuple, list, string, datetime, TimeSlot, TimeInterval, numpy datetime64, pandas timestamp)
101
102        *Returns* : None'''
103        slot = []
104        if isinstance(val, str):
105            try:
106                val = json.loads(val)
107            except:
108                val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
109                # try:    val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
110                # except: val = None
111        if val == None:
112            self.slot = slot
113            return
114        val = TimeSlot._listed(val)
115        #if isinstance(val, tuple): val = list(val)
116        if isinstance(val, list) and len(val) == 2 and not isinstance(val[0], TimeInterval):
117            try:
118                slot.append(TimeInterval(val))
119            except:
120                for interv in val:
121                    slot.append(TimeInterval(interv))
122        elif isinstance(val, list):
123            try:
124                for interv in val:
125                    slot.append(TimeInterval(interv))
126            except:
127                slot.append(TimeInterval(val))
128        elif isinstance(val, datetime.datetime):
129            slot.append(TimeInterval(val))
130        elif isinstance(val, TimeSlot):
131            slot = val.slot
132        elif isinstance(val, TimeInterval):
133            slot.append(val)
134        else:
135            slot.append(TimeInterval(val))
136        self.slot = TimeSlot._reduced(slot)
137
138    def __add__(self, other):
139        ''' Add other's values to self's values in a new TimeSlot'''
140        return TimeSlot(TimeSlot._reduced(self.slot + other.slot))
141
142    def __iadd__(self, other):
143        ''' Add other's values to self's values'''
144        self.slot = self._reduced(self.slot + other.slot)
145
146    def __contains__(self, item):
147        ''' item of extval'''
148        return item in self.slot
149
150    def __getitem__(self, index):
151        ''' return interval item'''
152        return self.slot[index]
153
154    def __setitem__(self, index, interv):
155        ''' modify interval item'''
156        if index < 0 or index >= len(self):
157            raise TimeSlotError("out of bounds")
158        self.slot[index] = TimeInterval(interv)
159        self.slot = TimeSlot._reduced(self.slot)
160
161    def __len__(self):
162        '''return the number of intervals included'''
163        return len(self.slot)
164
165    # def __repr__(self):
166    def __str__(self):
167        ''' return the type of slot and the json representation'''
168        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
169
170    def __repr__(self):
171        # return self.__class__.__name__ + f'({self.slot})'
172        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
173
174    def __eq__(self, other):
175        '''equal if the slots are equals'''
176        try:
177            return self.slot == other.slot
178        except:
179            return False
180
181    def __lt__(self, other):
182        '''compare the earliest dates'''
183        return self.slot[0] < other.slot[0]
184
185    def __hash__(self):
186        return sum(hash(interv) for interv in self.slot)
187        # return hash(self.json(True))
188
189    @property
190    def Bounds(self):
191        '''return an interval TimeSlot with the bounds of the TimeSlot object'''
192        return TimeSlot(self.bounds)
193
194    @property
195    def bounds(self):
196        '''return a tuple with the start and end dates with isoformat string'''
197        return (TimeSlot.form(self.slot[0].start), TimeSlot.form(self.slot[len(self) - 1].end))
198
199    @classmethod
200    def cast(cls, value):
201        '''
202        tranform a value (unique or list) in a list of `TimeSlot`
203
204        *Parameters*
205
206        - **value** : value to transform
207
208        *Returns*
209
210        - **list** : list of `TimeSlot`
211        '''
212        if isinstance(value, list):
213            try:
214                return [cls(val) for val in value]
215            except:
216                return [cls(value)]
217        else:
218            return [cls(value)]
219
220    @property
221    def Centroid(self):
222        '''return a TimeSlot with the date corresponding to the middle of the duration'''
223        return TimeSlot(self.instant)
224
225    @property
226    def duration(self):
227        '''cumulative duration of each interval (timedelta format)'''
228        duration = datetime.timedelta()
229        for interv in self.slot:
230            duration += interv.duration
231        return duration
232
233    @staticmethod
234    def form(dtime):
235        if dtime.timetuple()[3:6] == (0, 0, 0):
236            return dtime.date().isoformat()
237        return dtime.isoformat()
238
239    @property
240    def instant(self):
241        '''return the date corresponding to the middle of the duration (datetime format)'''
242        duration = self.duration / 2
243        for interv in self.slot:
244            if duration > interv.duration:
245                duration -= interv.duration
246            else:
247                return interv.start + duration
248
249    @property
250    def middle(self):
251        '''return the date corresponding to the middle of the bounds (datetime format)'''
252        return self.bounds.instant
253
254    @property
255    def name(self):
256        ''' class name'''
257        return self.__class__.__name__
258
259    @property
260    def interval(self):
261        '''return a list with the start and end dates (datetime format)'''
262        return [self.slot[0].start, self.slot[len(self) - 1].end]
263
264    @property
265    def stype(self):
266        '''return a string with the type of TimeSlot (instant, interval, slot)'''
267        if len(self.slot) == 1:
268            return self.slot[0].stype
269        else:
270            return 'slot'
271
272    def json(self, **kwargs):
273        '''
274        Return json/bson structure with the list of TimeInterval.
275
276        *Parameters*
277
278        - **encoded** : defaut False - if False return dict, else return json string/bson bytes
279        - **encode_format** : defaut 'json' - return json, bson or cbor format
280
281        *Returns* : string or dict'''
282        option = {'encoded': False, 'encode_format': 'json'} | kwargs
283        if len(self) == 1:
284            js = self.slot[0].json(
285                encoded=False, encode_format=option['encode_format'])
286        else:
287            js = [interv.json(
288                encoded=False, encode_format=option['encode_format']) for interv in self.slot]
289        if option['encoded'] and option['encode_format'] == 'json':
290            return json.dumps(js, cls=TimeSlotEncoder)
291        if option['encoded'] and option['encode_format'] == 'bson':
292            return bson.encode(js)
293        return js
294
295    def link(self, other):
296        '''
297        Return the status (string) of the link between two TimeSlot (self and other).
298        - equals     : if self and other are the same
299        - disjoint   : if self's intervals and other's intervals are all disjoint
300        - within     : if all self's intervals are included in other's intervals
301        - contains   : if all other's intervals are included in self's intervals
302        - intersects : in the others cases
303
304        *Parameters*
305
306        - **other** : TimeSlot to be compared
307
308        *Returns* 
309
310        - **tuple** : (string(status), boolean(full or not))'''
311        if self.stype == 'instant':
312            point, oslot = self[0], other
313        elif other.stype == 'instant':
314            point, oslot = other[0], self
315        else:
316            point = None
317        if point is not None:
318            contains = equals = False
319            for interv in oslot:
320                contains = contains or interv.link(point) == 'contains'
321                equals = equals or interv.link(point) == 'equals'
322            if equals and not contains:
323                return ('equals', True)
324            if contains and point == other[0]:
325                return ('contains', True)
326            if contains and point == self[0]:
327                return ('within', True)
328            return ('disjoint', True)
329        else:
330            union = self + other
331            link = 'intersects'
332            full = True
333            if union.duration == self.duration == other.duration:
334                full = len(union) == len(self) == len(other)
335                link = 'equals'
336            elif union.duration == self.duration:
337                full = len(union) == len(self)
338                link = 'contains'
339            elif union.duration == other.duration:
340                full = len(union) == len(other)
341                link = 'within'
342            elif union.duration == self.duration + other.duration:
343                full = len(union) == len(self) + len(other)
344                link = 'disjoint'
345            return (link, full)
346
347    def timetuple(self, index=0, encoded=False):
348        '''
349        Return json structure with the list of TimeInterval (timetuple filter).
350
351        *Parameters*
352
353        - **index** : integer, defaut 0 - timetuple format to apply :
354            - 0 : year
355            - 1 : month
356            - 2 : day
357            - 3 : hour
358            - 4 : minute
359            - 5 : seconds
360            - 6 : weekday
361            - 7 : yearday
362            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
363        - **encoded** : defaut False - if True return string, else return dict
364
365        *Returns* : string or dict'''
366        if len(self) == 1:
367            js = self.slot[0].timetuple(index, False)
368        else:
369            js = [interv.timetuple(index, False) for interv in self.slot]
370        if encoded:
371            return json.dumps(js, cls=TimeSlotEncoder)
372        else:
373            return js
374
375    def union(self, other):
376        ''' Add other's values to self's values in a new TimeSlot (same as __add__)'''
377        return self.__add__(other)
378
379    @staticmethod
380    def _reduced(listinterv):
381        ''' return an ordered and non-overlapping list of TimeInterval from any TimeInterval list'''
382        if not isinstance(listinterv, list) or len(listinterv) == 0:
383            return []
384        union = []
385        slot = sorted(listinterv)
386        interv = slot[0]
387        i = j = 0
388        while i < len(slot):
389            for j in range(i + 1, len(slot)):
390                if interv.link(slot[j]) == 'within':
391                    interv = slot[j]
392                elif interv.link(slot[j]) == 'intersects':
393                    interv = interv.union(slot[j])
394                elif interv.link(slot[j]) == 'disjoint':
395                    union.append(interv)
396                    interv = slot[j]
397                    i = j
398                    break
399            if j >= len(slot) - 1:
400                union.append(interv)
401                break
402        return union
403
404    @staticmethod
405    def _listed(idx):
406        '''transform a tuple of tuple in a list of list'''
407        if not isinstance(idx, str) and hasattr(idx, '__iter__'):
408            return [val if not isinstance(val, tuple) else TimeSlot._listed(val) for val in idx]
409        return idx
410
411
412class TimeInterval:    # !!! interval
413    '''        
414    *Attributes (for @property see methods)* :
415
416    - **start** : datetime Object - start of `TimeInterval`
417    - **end**   : datetime Object - end of `TimeInterval`
418
419    The methods defined in this class are : 
420
421    *dynamic value property (getters)*
422
423    - `TimeInterval.Bounds`
424    - `TimeInterval.bounds`
425    - `TimeInterval.Centroid`
426    - `TimeInterval.duration`
427    - `TimeInterval.instant`
428    - `TimeInterval.stype`
429
430    *instance methods*
431
432    - `TimeInterval.json`
433    - `TimeInterval.link`
434    - `TimeInterval.timetuple`
435    - `TimeInterval.union`
436    '''
437
438    def __init__(self, val=ES.nullDate):
439        '''
440        TimeInterval constructor.
441
442        *Parameters*
443
444        - **val** : date, interval (default ES.nullDate) - with several formats 
445        (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)
446
447        *Returns* : None'''
448        self.start = self.end = ES.nullDate
449        if isinstance(val, str):
450            try:
451                sl = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
452                if sl != None:
453                    self.start = self.end = sl
454                return
455            except:
456                try:
457                    val = json.loads(val)
458                except:
459                    val = ES.nullDate
460        if isinstance(val, list):
461            self._initInterval(val)
462        elif isinstance(val, TimeInterval):
463            self.start, self.end = val.start, val.end
464        else:
465            dat = self._initDat(val)
466            if dat != None:
467                self.start = self.end = dat
468
469    # def __repr__(self):
470    def __str__(self):
471        ''' return the type of interval and the json representation'''
472        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
473
474    def __repr__(self):
475        # if self.stype == 'instant' : return self.__class__.__name__ + f'("{self.start}")'
476        # return self.__class__.__name__ + f'(["{self.start}","{self.end}"])'
477        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
478
479    def __eq__(self, other):
480        '''equal if the 'start' and 'end' dates are equals'''
481        return self.start == other.start and self.end == other.end
482
483    def __lt__(self, other):
484        '''compare the earliest dates (start)'''
485        return self.start < other.start
486
487    def __hash__(self):
488        return hash(self.start) + hash(self.end)
489        # return hash(self.json(True))
490
491    @property
492    def bounds(self):
493        '''return a tuple with the start and end dates with isoformat string'''
494        return (TimeSlot.form(self.start), TimeSlot.form(self.end))
495
496    @property
497    def Centroid(self):
498        '''return a TimeInterval with the date corresponding to the middle of the interval'''
499        return TimeInterval(self.instant)
500
501    @property
502    def duration(self):
503        '''duration between 'end' and 'start' date (timedelta format)'''
504        return self.end - self.start
505
506    @property
507    def instant(self):
508        '''return the date corresponding to the middle of the duration (datetime format)'''
509        return self.start + (self.end - self.start) / 2
510
511    @property
512    def stype(self):
513        '''return a string with the type of TimeInterval (instant, interval)'''
514        if self.start == self.end:
515            return 'instant'
516        return 'interval'
517
518    def json(self, encoded=False, encode_format='json'):
519        '''
520        Return json/bson structure (date if 'instant' or [start, end] if 'interval') 
521        with datetime or datetime.isoformat for dates.
522
523        *Parameters*
524
525        - **encoded** : defaut False - if True return dict, else return json string/bson bytes
526        - **encode_format**   : defaut 'json' - return json, bson or cbor format
527
528        *Returns* : string or dict'''
529        if self.stype == 'instant':
530            js = self.start
531        else:
532            js = [self.start, self.end]
533        '''if   self.stype == 'instant' : 
534            if encode_format == 'bson':  js = self.start
535            else:           
536                js = TimeSlot.form(self.start)
537        elif self.stype == 'interval' : 
538            if encode_format == 'bson':  js = [self.start, self.end]
539            else:           js = [TimeSlot.form(self.start), TimeSlot.form(self.end)]'''
540        if encoded and encode_format == 'json':
541            return json.dumps(js, cls=TimeSlotEncoder)
542        if encoded and encode_format == 'bson':
543            return bson.encode(js)
544        return js
545
546    def link(self, other):
547        '''
548        Return the status (string) of the link between two TimeIntervals (self and other).
549        - equals     : if self and other are the same
550        - disjoint   : if self's interval and other's interval are disjoint
551        - within     : if other's interval is included in self's interval
552        - contains   : if self's interval is included in other's interval
553        - intersects : in the others cases
554
555        *Parameters*
556
557        - **other** : TimeInterval to be compared
558
559        *Returns* : string'''
560        if self.start == other.start and self.end == other.end:
561            return 'equals'
562        if self.start <= other.start and self.end >= other.end:
563            return 'contains'
564        if self.start >= other.start and self.end <= other.end:
565            return 'within'
566        if self.start <= other.end and self.end >= other.start:
567            return 'intersects'
568        return 'disjoint'
569
570    def timetuple(self, index=0, encoded=False):
571        '''
572        Return json structure (timetuple filter).
573
574        *Parameters*
575
576        - **index** : integer, defaut 0 - timetuple format to apply :
577            - 0 : year
578            - 1 : month
579            - 2 : day
580            - 3 : hour
581            - 4 : minute
582            - 5 : seconds
583            - 6 : weekday
584            - 7 : yearday
585            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
586        - **encoded** : defaut False - if True return string, else return dict
587
588        *Returns* : string or dict'''
589        if index not in [0, 1, 2, 3, 4, 5, 6, 7, 8]:
590            return None
591        if self.stype == 'instant':
592            js = self.start.timetuple()[index]
593        elif self.stype == 'interval':
594            js = [self.start.timetuple()[index], self.end.timetuple()[index]]
595        if encoded:
596            return json.dumps(js, cls=TimeSlotEncoder)
597        else:
598            return js
599
600    def union(self, other):
601        ''' Add other's values to self's values in a new TimeInterval 
602        if self and other are not disjoint'''
603        if self.link(other) != 'disjoint':
604            return TimeInterval([min(self.start, other.start), max(self.end, other.end)])
605        else:
606            return None
607
608    def _initInterval(self, val):
609        '''initialization of start and end dates from a list'''
610        self.start = self.end = self._initDat(val[0])
611        if len(val) > 1:
612            self.end = self._initDat(val[1])
613        else:
614            self.start = self.end = self._initDat(val)
615        if self.end < self.start:
616            self.start, self.end = self.end, self.start
617
618    def _initDat(self, val):
619        '''initialization of start and end dates from a unique value 
620        (datetime, string, numpy.datetime64, pandas Timestamp)'''
621        if isinstance(val, datetime.datetime):
622            res = val
623            '''if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
624                res = val.astimezone(datetime.timezone.utc)
625            else: res = val'''
626        elif isinstance(val, str):
627            try:
628                res = datetime.datetime.fromisoformat(val)
629            except:
630                res = ES.nullDate
631        elif isinstance(val, numpy.datetime64):
632            res = pandas.Timestamp(val).to_pydatetime()
633        elif isinstance(val, pandas._libs.tslibs.timestamps.Timestamp):
634            res = val.to_pydatetime()
635        else:
636            raise TimeSlotError("impossible to convert in a date")
637        return TimeInterval._dattz(res)
638
639    @staticmethod
640    def _dattz(val):
641        if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
642            return val.replace(tzinfo=datetime.timezone.utc)
643        return val
644
645
646class TimeSlotError(Exception):
647    pass
class TimeSlotEncoder(json.encoder.JSONEncoder):
58class TimeSlotEncoder(json.JSONEncoder):
59    """add a new json encoder for TimeSlot"""
60
61    def default(self, o):
62        if isinstance(o, datetime.datetime):
63            return o.isoformat()
64        return json.JSONEncoder.default(self, o)

add a new json encoder for TimeSlot

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

Attributes (for @property see methods) :

The methods defined in this class are :

dynamic value property (getters)

instance methods

TimeSlot(val=None)
 94    def __init__(self, val=None):
 95        '''
 96        TimeSlot constructor.
 97
 98        *Parameters*
 99
100        - **val** : date, interval, list of interval (default None) - with several formats 
101        (tuple, list, string, datetime, TimeSlot, TimeInterval, numpy datetime64, pandas timestamp)
102
103        *Returns* : None'''
104        slot = []
105        if isinstance(val, str):
106            try:
107                val = json.loads(val)
108            except:
109                val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
110                # try:    val = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
111                # except: val = None
112        if val == None:
113            self.slot = slot
114            return
115        val = TimeSlot._listed(val)
116        #if isinstance(val, tuple): val = list(val)
117        if isinstance(val, list) and len(val) == 2 and not isinstance(val[0], TimeInterval):
118            try:
119                slot.append(TimeInterval(val))
120            except:
121                for interv in val:
122                    slot.append(TimeInterval(interv))
123        elif isinstance(val, list):
124            try:
125                for interv in val:
126                    slot.append(TimeInterval(interv))
127            except:
128                slot.append(TimeInterval(val))
129        elif isinstance(val, datetime.datetime):
130            slot.append(TimeInterval(val))
131        elif isinstance(val, TimeSlot):
132            slot = val.slot
133        elif isinstance(val, TimeInterval):
134            slot.append(val)
135        else:
136            slot.append(TimeInterval(val))
137        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

slot
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):
200    @classmethod
201    def cast(cls, value):
202        '''
203        tranform a value (unique or list) in a list of `TimeSlot`
204
205        *Parameters*
206
207        - **value** : value to transform
208
209        *Returns*
210
211        - **list** : list of `TimeSlot`
212        '''
213        if isinstance(value, list):
214            try:
215                return [cls(val) for val in value]
216            except:
217                return [cls(value)]
218        else:
219            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):
234    @staticmethod
235    def form(dtime):
236        if dtime.timetuple()[3:6] == (0, 0, 0):
237            return dtime.date().isoformat()
238        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):
273    def json(self, **kwargs):
274        '''
275        Return json/bson structure with the list of TimeInterval.
276
277        *Parameters*
278
279        - **encoded** : defaut False - if False return dict, else return json string/bson bytes
280        - **encode_format** : defaut 'json' - return json, bson or cbor format
281
282        *Returns* : string or dict'''
283        option = {'encoded': False, 'encode_format': 'json'} | kwargs
284        if len(self) == 1:
285            js = self.slot[0].json(
286                encoded=False, encode_format=option['encode_format'])
287        else:
288            js = [interv.json(
289                encoded=False, encode_format=option['encode_format']) for interv in self.slot]
290        if option['encoded'] and option['encode_format'] == 'json':
291            return json.dumps(js, cls=TimeSlotEncoder)
292        if option['encoded'] and option['encode_format'] == 'bson':
293            return bson.encode(js)
294        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):
348    def timetuple(self, index=0, encoded=False):
349        '''
350        Return json structure with the list of TimeInterval (timetuple filter).
351
352        *Parameters*
353
354        - **index** : integer, defaut 0 - timetuple format to apply :
355            - 0 : year
356            - 1 : month
357            - 2 : day
358            - 3 : hour
359            - 4 : minute
360            - 5 : seconds
361            - 6 : weekday
362            - 7 : yearday
363            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
364        - **encoded** : defaut False - if True return string, else return dict
365
366        *Returns* : string or dict'''
367        if len(self) == 1:
368            js = self.slot[0].timetuple(index, False)
369        else:
370            js = [interv.timetuple(index, False) for interv in self.slot]
371        if encoded:
372            return json.dumps(js, cls=TimeSlotEncoder)
373        else:
374            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):
376    def union(self, other):
377        ''' Add other's values to self's values in a new TimeSlot (same as __add__)'''
378        return self.__add__(other)

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

class TimeInterval:
413class TimeInterval:    # !!! interval
414    '''        
415    *Attributes (for @property see methods)* :
416
417    - **start** : datetime Object - start of `TimeInterval`
418    - **end**   : datetime Object - end of `TimeInterval`
419
420    The methods defined in this class are : 
421
422    *dynamic value property (getters)*
423
424    - `TimeInterval.Bounds`
425    - `TimeInterval.bounds`
426    - `TimeInterval.Centroid`
427    - `TimeInterval.duration`
428    - `TimeInterval.instant`
429    - `TimeInterval.stype`
430
431    *instance methods*
432
433    - `TimeInterval.json`
434    - `TimeInterval.link`
435    - `TimeInterval.timetuple`
436    - `TimeInterval.union`
437    '''
438
439    def __init__(self, val=ES.nullDate):
440        '''
441        TimeInterval constructor.
442
443        *Parameters*
444
445        - **val** : date, interval (default ES.nullDate) - with several formats 
446        (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)
447
448        *Returns* : None'''
449        self.start = self.end = ES.nullDate
450        if isinstance(val, str):
451            try:
452                sl = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
453                if sl != None:
454                    self.start = self.end = sl
455                return
456            except:
457                try:
458                    val = json.loads(val)
459                except:
460                    val = ES.nullDate
461        if isinstance(val, list):
462            self._initInterval(val)
463        elif isinstance(val, TimeInterval):
464            self.start, self.end = val.start, val.end
465        else:
466            dat = self._initDat(val)
467            if dat != None:
468                self.start = self.end = dat
469
470    # def __repr__(self):
471    def __str__(self):
472        ''' return the type of interval and the json representation'''
473        return self.stype + '\n' + self.json(encoded=True, encode_format='json')
474
475    def __repr__(self):
476        # if self.stype == 'instant' : return self.__class__.__name__ + f'("{self.start}")'
477        # return self.__class__.__name__ + f'(["{self.start}","{self.end}"])'
478        return self.__class__.__name__ + '(' + self.json(encoded=True, encode_format='json') + ')'
479
480    def __eq__(self, other):
481        '''equal if the 'start' and 'end' dates are equals'''
482        return self.start == other.start and self.end == other.end
483
484    def __lt__(self, other):
485        '''compare the earliest dates (start)'''
486        return self.start < other.start
487
488    def __hash__(self):
489        return hash(self.start) + hash(self.end)
490        # return hash(self.json(True))
491
492    @property
493    def bounds(self):
494        '''return a tuple with the start and end dates with isoformat string'''
495        return (TimeSlot.form(self.start), TimeSlot.form(self.end))
496
497    @property
498    def Centroid(self):
499        '''return a TimeInterval with the date corresponding to the middle of the interval'''
500        return TimeInterval(self.instant)
501
502    @property
503    def duration(self):
504        '''duration between 'end' and 'start' date (timedelta format)'''
505        return self.end - self.start
506
507    @property
508    def instant(self):
509        '''return the date corresponding to the middle of the duration (datetime format)'''
510        return self.start + (self.end - self.start) / 2
511
512    @property
513    def stype(self):
514        '''return a string with the type of TimeInterval (instant, interval)'''
515        if self.start == self.end:
516            return 'instant'
517        return 'interval'
518
519    def json(self, encoded=False, encode_format='json'):
520        '''
521        Return json/bson structure (date if 'instant' or [start, end] if 'interval') 
522        with datetime or datetime.isoformat for dates.
523
524        *Parameters*
525
526        - **encoded** : defaut False - if True return dict, else return json string/bson bytes
527        - **encode_format**   : defaut 'json' - return json, bson or cbor format
528
529        *Returns* : string or dict'''
530        if self.stype == 'instant':
531            js = self.start
532        else:
533            js = [self.start, self.end]
534        '''if   self.stype == 'instant' : 
535            if encode_format == 'bson':  js = self.start
536            else:           
537                js = TimeSlot.form(self.start)
538        elif self.stype == 'interval' : 
539            if encode_format == 'bson':  js = [self.start, self.end]
540            else:           js = [TimeSlot.form(self.start), TimeSlot.form(self.end)]'''
541        if encoded and encode_format == 'json':
542            return json.dumps(js, cls=TimeSlotEncoder)
543        if encoded and encode_format == 'bson':
544            return bson.encode(js)
545        return js
546
547    def link(self, other):
548        '''
549        Return the status (string) of the link between two TimeIntervals (self and other).
550        - equals     : if self and other are the same
551        - disjoint   : if self's interval and other's interval are disjoint
552        - within     : if other's interval is included in self's interval
553        - contains   : if self's interval is included in other's interval
554        - intersects : in the others cases
555
556        *Parameters*
557
558        - **other** : TimeInterval to be compared
559
560        *Returns* : string'''
561        if self.start == other.start and self.end == other.end:
562            return 'equals'
563        if self.start <= other.start and self.end >= other.end:
564            return 'contains'
565        if self.start >= other.start and self.end <= other.end:
566            return 'within'
567        if self.start <= other.end and self.end >= other.start:
568            return 'intersects'
569        return 'disjoint'
570
571    def timetuple(self, index=0, encoded=False):
572        '''
573        Return json structure (timetuple filter).
574
575        *Parameters*
576
577        - **index** : integer, defaut 0 - timetuple format to apply :
578            - 0 : year
579            - 1 : month
580            - 2 : day
581            - 3 : hour
582            - 4 : minute
583            - 5 : seconds
584            - 6 : weekday
585            - 7 : yearday
586            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
587        - **encoded** : defaut False - if True return string, else return dict
588
589        *Returns* : string or dict'''
590        if index not in [0, 1, 2, 3, 4, 5, 6, 7, 8]:
591            return None
592        if self.stype == 'instant':
593            js = self.start.timetuple()[index]
594        elif self.stype == 'interval':
595            js = [self.start.timetuple()[index], self.end.timetuple()[index]]
596        if encoded:
597            return json.dumps(js, cls=TimeSlotEncoder)
598        else:
599            return js
600
601    def union(self, other):
602        ''' Add other's values to self's values in a new TimeInterval 
603        if self and other are not disjoint'''
604        if self.link(other) != 'disjoint':
605            return TimeInterval([min(self.start, other.start), max(self.end, other.end)])
606        else:
607            return None
608
609    def _initInterval(self, val):
610        '''initialization of start and end dates from a list'''
611        self.start = self.end = self._initDat(val[0])
612        if len(val) > 1:
613            self.end = self._initDat(val[1])
614        else:
615            self.start = self.end = self._initDat(val)
616        if self.end < self.start:
617            self.start, self.end = self.end, self.start
618
619    def _initDat(self, val):
620        '''initialization of start and end dates from a unique value 
621        (datetime, string, numpy.datetime64, pandas Timestamp)'''
622        if isinstance(val, datetime.datetime):
623            res = val
624            '''if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
625                res = val.astimezone(datetime.timezone.utc)
626            else: res = val'''
627        elif isinstance(val, str):
628            try:
629                res = datetime.datetime.fromisoformat(val)
630            except:
631                res = ES.nullDate
632        elif isinstance(val, numpy.datetime64):
633            res = pandas.Timestamp(val).to_pydatetime()
634        elif isinstance(val, pandas._libs.tslibs.timestamps.Timestamp):
635            res = val.to_pydatetime()
636        else:
637            raise TimeSlotError("impossible to convert in a date")
638        return TimeInterval._dattz(res)
639
640    @staticmethod
641    def _dattz(val):
642        if val.tzinfo is None or val.tzinfo.utcoffset(val) is None:
643            return val.replace(tzinfo=datetime.timezone.utc)
644        return val

Attributes (for @property see methods) :

The methods defined in this class are :

dynamic value property (getters)

instance methods

TimeInterval(val=None)
439    def __init__(self, val=ES.nullDate):
440        '''
441        TimeInterval constructor.
442
443        *Parameters*
444
445        - **val** : date, interval (default ES.nullDate) - with several formats 
446        (list, string, datetime, TimeInterval, numpy datetime64, pandas timestamp)
447
448        *Returns* : None'''
449        self.start = self.end = ES.nullDate
450        if isinstance(val, str):
451            try:
452                sl = TimeInterval._dattz(datetime.datetime.fromisoformat(val))
453                if sl != None:
454                    self.start = self.end = sl
455                return
456            except:
457                try:
458                    val = json.loads(val)
459                except:
460                    val = ES.nullDate
461        if isinstance(val, list):
462            self._initInterval(val)
463        elif isinstance(val, TimeInterval):
464            self.start, self.end = val.start, val.end
465        else:
466            dat = self._initDat(val)
467            if dat != None:
468                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'):
519    def json(self, encoded=False, encode_format='json'):
520        '''
521        Return json/bson structure (date if 'instant' or [start, end] if 'interval') 
522        with datetime or datetime.isoformat for dates.
523
524        *Parameters*
525
526        - **encoded** : defaut False - if True return dict, else return json string/bson bytes
527        - **encode_format**   : defaut 'json' - return json, bson or cbor format
528
529        *Returns* : string or dict'''
530        if self.stype == 'instant':
531            js = self.start
532        else:
533            js = [self.start, self.end]
534        '''if   self.stype == 'instant' : 
535            if encode_format == 'bson':  js = self.start
536            else:           
537                js = TimeSlot.form(self.start)
538        elif self.stype == 'interval' : 
539            if encode_format == 'bson':  js = [self.start, self.end]
540            else:           js = [TimeSlot.form(self.start), TimeSlot.form(self.end)]'''
541        if encoded and encode_format == 'json':
542            return json.dumps(js, cls=TimeSlotEncoder)
543        if encoded and encode_format == 'bson':
544            return bson.encode(js)
545        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):
571    def timetuple(self, index=0, encoded=False):
572        '''
573        Return json structure (timetuple filter).
574
575        *Parameters*
576
577        - **index** : integer, defaut 0 - timetuple format to apply :
578            - 0 : year
579            - 1 : month
580            - 2 : day
581            - 3 : hour
582            - 4 : minute
583            - 5 : seconds
584            - 6 : weekday
585            - 7 : yearday
586            - 8 : isdst (1 when daylight savings time is in effect, 0 when is not)
587        - **encoded** : defaut False - if True return string, else return dict
588
589        *Returns* : string or dict'''
590        if index not in [0, 1, 2, 3, 4, 5, 6, 7, 8]:
591            return None
592        if self.stype == 'instant':
593            js = self.start.timetuple()[index]
594        elif self.stype == 'interval':
595            js = [self.start.timetuple()[index], self.end.timetuple()[index]]
596        if encoded:
597            return json.dumps(js, cls=TimeSlotEncoder)
598        else:
599            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):
601    def union(self, other):
602        ''' Add other's values to self's values in a new TimeInterval 
603        if self and other are not disjoint'''
604        if self.link(other) != 'disjoint':
605            return TimeInterval([min(self.start, other.start), max(self.end, other.end)])
606        else:
607            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):
647class TimeSlotError(Exception):
648    pass

Common base class for all non-exit exceptions.

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback
args