J'ai besoin de calculer la durée entre une date spécifique et maintenant pour le nettoyage d'index elasticsearch. Mon travail fonctionnera en python. J'ai un fichier de configuration:
indices: - name: test template: raw* liveLength: 1d
Comment analyser la chaîne "1d" ou "2m" à un intervalle de temps valide pour calculer la durée à partir d'une date spécifique à partir du champ liveLength?
p>
3 Réponses :
J'ai trouvé le code sur GitHub:
from decimal import Decimal from datetime import timedelta def duration(duration_string): #example: '5d3h2m1s' duration_string = duration_string.lower() total_seconds = Decimal('0') prev_num = [] for character in duration_string: if character.isalpha(): if prev_num: num = Decimal(''.join(prev_num)) if character == 'd': total_seconds += num * 60 * 60 * 24 elif character == 'h': total_seconds += num * 60 * 60 elif character == 'm': total_seconds += num * 60 elif character == 's': total_seconds += num prev_num = [] elif character.isnumeric() or character == '.': prev_num.append(character) return timedelta(seconds=float(total_seconds))
Vous pouvez utiliser une expression régulière pour extraire les parties d'unité nombre / temps, puis rechercher un multiplicateur dans un dictionnaire. De cette façon, il est un peu plus court et probablement beaucoup plus lisible que votre analyse manuelle et if / elif
chain.
def duration(string): mult = {"s": 1, "m": 60, "h": 60*60, "d": 60*60*24} parts = re.findall(r"(\d+(?:\.\d)?)([smhd])", string) total_seconds = sum(float(x) * mult[m] for x, m in parts) return timedelta(seconds=total_seconds) print(duration("2d 4h 13m 5.2s")) # 2 days, 4:03:05.200000
En tant que fonction:
>>> mult = {"s": 1, "m": 60, "h": 60*60, "d": 60*60*24} >>> s = "2d 4h 13m 5.2s" >>> re.findall(r"(\d+(?:\.\d)?)([smhd])", s) [('2', 'd'), ('4', 'h'), ('3', 'm'), ('5.2', 's')] >>> sum(float(x) * mult[m] for x, m in _) 187385.2
Cela garantira également que la partie numérique est en fait un nombre valide ( et pas n'importe quelle séquence de chiffres et de points). En outre, cela lèvera une exception si des unités de temps autres que les unités de temps autorisées sont utilisées.
La fonction pourrait être encore optimisée en pré-compilant l'expression régulière avec re.compile
en dehors de la fonction. Quand je l'ai testé avec % timeit
d'IPython, le mien s'est avéré un peu plus rapide (2,1µs contre 2,8µs pour le vôtre, à la fois sans la création de timedelta
et avec juste float
au lieu de Decimal
). De plus, je considérerais cela comme plus lisible en ayant un style beaucoup plus déclaratif et moins impératif , mais c'est certainement une question de goût et de préférences.
Comment pouvons-nous compiler un benchmark? Pour moi, le code que j'ai trouvé est plus lisible.
@ozlevka Vraiment? Eh bien, je suppose que cela dépend de votre aisance avec la compréhension de dict, l'expression du générateur et, bien sûr, les expressions régulières. En fin de compte, vous devriez prendre tout ce avec quoi vous pouvez travailler. Mais notez, par exemple, que votre code considérerait également ... 2.3..4.5
un nombre valide ... mais si vous savez d'où vient l'entrée, cela ne doit pas être un problème.
@ozlevka Depuis que vous avez demandé un benchmark: % timeit
d'IPython montre que le mien est légèrement plus rapide (3,2µs contre 4,6µs), mais cela semble être en grande partie dû à votre utilisation de Decimal < / code>, ce qui n'est probablement pas nécessaire (vous reconvertissez de toute façon en
float
). Sans Decimal
, c'est 3,2 µs contre 3,5 µs.
Votre code s'exécute en moyenne en 0,001150 ms si je compile regex avant, puis exécute en moyenne environ 0,000900 ms. L'analyse directe s'exécute également en moyenne 0,000900 ms. Alors les performances sont les mêmes. Mais de toute façon je pense que ce code devrait être lisible par beaucoup de gens et ne pas prendre le temps de le comprendre.
@ozlevka Je suis tout à fait d'accord avec vous, la lisibilité est plus importante que la micro-optimisation des performances. Je considère simplement mon code comme plus lisible, car il est plus explicite à quoi les parties sont censées ressembler (en utilisant l'expression régulière au lieu d'être réparties sur de nombreuses lignes de boucle et if / elif) et comment elles sont additionnées. Mais encore une fois, vous devez choisir ce avec quoi vous pouvez travailler le mieux. J'essaie juste de suggérer une alternative.
Je suis d'accord avec vous sur le nombre de chaînes. Et la qualité du code. De plus, je peux admettre que votre code est plus élégant et court. Mais je dois expliquer ce que le code fait pour mes clients. Et votre solution rend cette tâche impossible :)
Voici ma solution; J'ai utilisé la bibliothèque python datetime
et c'est timedelta
:
import datetime intervals = { "w": datetime.timedelta(weeks=1), "d": datetime.timedelta(days=1), "h": datetime.timedelta(hours=1), "m": datetime.timedelta(minutes=1), "s": datetime.timedelta(seconds=1) } def parse_live_length(string): time_interval_start_index = 0 for char in string: if char.isnumeric(): time_interval_start_index += 1 else: return int(string[0:time_interval_start_index]), string[time_interval_start_index:] return False # "2w" used as an example live_length = "2w" time_scalar, ll_interval = parse_live_length(live_length) for interval in intervals: if interval == ll_interval: new_delta = time_scalar * intervals[interval] break # Example of how it could be used current = datetime.datetime.now() new_time = new_delta + current print(new_time.day, new_time.month, new_time.year)