Code source de pyspc.core.timeutil

#!/usr/bin/python3
# -*- coding: utf-8 -*-
########################################################################
#
# This file is part of python module <pyspc>.
# Copyright (C) 2013-2021  R. Marty
#   (renaud.marty@developpement-durable.gouv.fr)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program (see COPYING.txt).
# If not, see <http://www.gnu.org/licenses/>.
#
########################################################################
"""
Objets natifs et convention de pyspc - Manipulation de dates
"""
from collections import OrderedDict
from datetime import datetime as dt, timedelta as td

import pyspc.core.exception as _exception


[docs] def dtfmt(tdelta=None): """ Définir le format de la date selon le pas de temps Parameters ---------- tdelta : timedelta pas de temps Returns ------- dtfmt : str Format de la date """ # Pas de temps inconnu if tdelta is None: return '%Y%m%d%H%M' _exception.check_td(tdelta) # Pas de temps : MOIS if tdelta == td(days=31): return '%Y%m' # Pas de temps : multiple JOUR if tdelta % td(days=1) == td(0): return '%Y%m%d' # Pas de temps : multiple HEURE if tdelta % td(hours=1) == td(0): return '%Y%m%d%H' # Autres pas de temps return '%Y%m%d%H%M'
[docs] def dtheader(tdelta=None): """ Définir l'entête de la colonne des dates selon le pas de temps Parameters ---------- tdelta : timedelta pas de temps Returns ------- header : str Format de la date """ # Pas de temps inconnu if tdelta is None: return 'AAAAMMJJHHMM' _exception.check_td(tdelta) # Pas de temps : multiple JOUR if tdelta % td(days=1) == td(0): return 'AAAAMMJJ' # Pas de temps : multiple HEURE if tdelta % td(hours=1) == td(0): return 'AAAAMMJJHH' # Autres pas de temps return 'AAAAMMJJHHMM'
[docs] def lenstr2dtfmt(txt=None): """ Définir le format de la date selon la longueur de la chaine de caractères Parameters ---------- txt : str Chaine de caractères Returns ------- dtfmt : str Format de la date """ _exception.check_str(txt) dtstr = 'YYYYmmddHHMM' return dtstr[:len(txt)].replace('YYYY', '%Y').replace('mm', '%m')\ .replace('dd', '%d').replace('HH', '%H').replace('MM', '%M')
[docs] def str2dt(txt=None): """ Convertir une chaine de caractères en date Parameters ---------- txt : str Chaine de caractères : AAAAMMJJ[HH,[MM]] Returns ------- dt : datetime Date """ if txt is None: return None _exception.check_str(txt) return dt.strptime(txt, lenstr2dtfmt(txt))
# https://nedbatchelder.com/blog/201310/range_overlap_in_two_compares.html
[docs] def overlap(start1, end1, start2, end2): """Does the range (start1, end1) overlap with (start2, end2)?""" return not (end1 < start2 or end2 < start1)
# return end1 >= start2 and end2 >= start1
[docs] def group_events(events=None, tol=None): """ Grouper des événements qui se chevauchent Parameters ---------- events : list Liste de événements (date début, date fin) Other Parameters ---------------- tol : datetime.timedelta Tolérance pour former un groupe. Par défaut: 0 seconde Returns ------- groups : list Liste des événements après re-oganisation Examples: --------- >>> >>> events = [ ... (dt(2001, 1, 1), dt(2001, 1, 10)), ... (dt(2001, 2, 1), dt(2001, 2, 10)), ... (dt(2001, 1, 25), dt(2001, 2, 5)), ... (dt(2001, 2, 2), dt(2001, 2, 2)), ... (dt(2001, 2, 10), dt(2001, 2, 12)), ... (dt(2002, 1, 1), dt(2002, 1, 10)), ... ] >>> groups_of_event = group_events(events=events) >>> groups_of_event [(datetime.datetime(2001, 1, 1), datetime.datetime(2001, 1, 10)), (datetime.datetime(2001, 1, 25), datetime.datetime(2001, 2, 12)), (datetime.datetime(2002, 1, 1), datetime.datetime(2002, 1, 10))] """ if tol is None: tol = td(seconds=0) # 1 heure _exception.check_td(tol) elements = [list(e) for e in events] for j in range(len(elements)-1): for k in range(j+1, len(elements)): if elements[j] is None: continue ej = list(elements[j]) ej[0] -= tol ej[1] += tol if overlap(*ej, *elements[k]): elements[k][0] = min(elements[j][0], elements[k][0]) elements[k][1] = max(elements[j][1], elements[k][1]) elements[j] = None return sorted([tuple(e) for e in elements if e is not None])
# https://python-forum.io/thread-22725.html # https://stackoverflow.com/questions/20112143/clustering-grouping-a-list-based-on-time-python
[docs] def group_dates(dates=None, tol=None): """ Grouper des dates selon une tolérance Parameters ---------- dates : list Liste de dates Other Parameters ---------------- tol : datetime.timedelta Tolérance pour former un groupe. Par défaut: 0 seconde Returns ------- groups : list Groupes de dates triées chronologiquement """ if tol is None: tol = td(seconds=0) # 1 heure _exception.check_td(tol) ds = sorted(list(set(dates))) diff = [td(seconds=0)] + [ds[i+1] - ds[i] for i in range(len(ds)-1)] i = 0 keys = [] for x in diff: if x > tol: i += 1 keys.append(i) groups = OrderedDict() for d, k in zip(ds, keys): groups.setdefault(k, []) groups[k].append(d) return list(groups.values())