|
6 | 6 | import os |
7 | 7 | import re |
8 | 8 | import sys |
9 | | -from datetime import datetime, timezone |
| 9 | +from datetime import datetime |
10 | 10 | from enum import Enum |
11 | 11 | from importlib.util import find_spec |
12 | 12 | from ipaddress import ip_network |
13 | 13 | from itertools import groupby |
14 | 14 | from logging.handlers import RotatingFileHandler |
15 | 15 | from os import getenv |
| 16 | +from time import time |
16 | 17 | from typing import Any, Dict, List, Tuple |
17 | 18 | from tzlocal import get_localzone |
18 | 19 |
|
@@ -381,41 +382,61 @@ def calc_avg(oldval, newval): |
381 | 382 | return float((oldval+newval)/2) |
382 | 383 |
|
383 | 384 |
|
384 | | -def parse_relative_timestamp(uptime: str, relative_to: int = None) -> int: |
| 385 | +def parse_relative_timestamp( |
| 386 | + uptime: str, relative_to: int = None, ms=False) -> int: |
385 | 387 | """Get a relative time (i.e. with format 10 weeks, 4 days, 3 hours 11 mins) |
386 | 388 | and convert it into a timestamp. |
387 | 389 |
|
388 | 390 | Args: |
389 | 391 | uptime (str): _description_ |
390 | 392 | relative_to (int, optional): provide a custom base epoch timestamp, if |
391 | | - not provided, the base timestamp is "now". |
| 393 | + not provided, the base timestamp is "now". Time in s or ms |
| 394 | + depending on the value of the `ms` argument. |
| 395 | + ms (bool, optional): whether the function assumes time in seconds or |
| 396 | + milliseconds. Default to False. |
392 | 397 |
|
393 | 398 | Returns: |
394 | | - int: The epoch timestamp of the base time minus the uptime |
| 399 | + int: The epoch timestamp of the base time minus the uptime. Returned |
| 400 | + time in ms or s depending if ms=True |
395 | 401 | """ |
| 402 | + # We do not use the RELATIVE_BASE option of dateparser due to a bug in the |
| 403 | + # library causing a memory leak: |
| 404 | + # https://github.com/scrapinghub/dateparser/issues/985 |
| 405 | + |
396 | 406 | settings = {'TIMEZONE': 'utc', |
397 | 407 | 'RETURN_AS_TIMEZONE_AWARE': True} |
398 | | - if relative_to: |
399 | | - base_ts = datetime.fromtimestamp(relative_to, timezone.utc) |
400 | | - settings['RELATIVE_BASE'] = base_ts |
| 408 | + conversion_value = 1000 if ms else 1 |
| 409 | + |
| 410 | + parsed_uptime = parse(uptime, settings=settings) |
| 411 | + if not parsed_uptime: |
| 412 | + return None |
| 413 | + |
| 414 | + ts_offset = time() - (relative_to / conversion_value) if relative_to else 0 |
401 | 415 |
|
402 | | - return int(parse(uptime, settings=settings).timestamp()) |
| 416 | + return int((parsed_uptime.timestamp() - ts_offset) * conversion_value) |
403 | 417 |
|
404 | 418 |
|
405 | | -def get_timestamp_from_cisco_time(in_data, timestamp) -> int: |
| 419 | +def get_timestamp_from_cisco_time(in_data: str, timestamp: int) -> int: |
406 | 420 | """Get timestamp in ms from the Cisco-specific timestamp string |
407 | 421 | Examples of Cisco timestamp str are P2DT14H45M16S, P1M17DT4H49M50S etc. |
| 422 | +
|
| 423 | + Args: |
| 424 | + in_data (str): The Cisco uptime string |
| 425 | + timestamp (int): the base unix timestamp IS SECONDS from which |
| 426 | + subtracting the uptime. |
| 427 | +
|
| 428 | + Returns: |
| 429 | + int: a unix timestamp in milliseconds |
408 | 430 | """ |
409 | 431 | if in_data and not in_data.startswith('P'): |
410 | 432 | in_data = in_data.replace('y', 'years') |
411 | 433 | in_data = in_data.replace('w', 'weeks') |
412 | 434 | in_data = in_data.replace('d', 'days') |
413 | 435 |
|
414 | | - other_time = parse(in_data, |
415 | | - settings={'RELATIVE_BASE': |
416 | | - datetime.utcfromtimestamp(timestamp)}) |
| 436 | + ts_offset = time() - timestamp |
| 437 | + other_time = parse(in_data) |
417 | 438 | if other_time: |
418 | | - return int(other_time.timestamp()*1000) |
| 439 | + return int((other_time.timestamp() - ts_offset) * 1000) |
419 | 440 | else: |
420 | 441 | logger.error(f'Unable to parse relative time string, {in_data}') |
421 | 442 | return 0 |
|
0 commit comments