From 507999b117b024fed1ccf067bd5dc411dbeb268c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Mon, 29 Mar 2021 14:45:13 -0700 Subject: [PATCH] add stat command timestamp detection --- docs/utils.md | 32 ++++++++++++++++---------------- jc/utils.py | 13 +++++++++++-- tests/test_utils.py | 8 +++++++- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index 2ea95347..ec8a152d 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -15,7 +15,7 @@ Parameters: Returns: - no return, just prints output to STDERR + None - just prints output to STDERR ## error_message @@ -31,7 +31,7 @@ Parameters: Returns: - no return, just prints output to STDERR + None - just prints output to STDERR ## compatibility @@ -50,7 +50,7 @@ Parameters: Returns: - no return, just prints output to STDERR + None - just prints output to STDERR ## has_data @@ -78,23 +78,23 @@ Input a date-time text string of several formats and convert to a naive or timez Parameters: - data: (string) a string representation of a date-time in several supported formats + data: (string) a string representation of a date-time in several supported formats Returns: - Dict/None A Dictionary of the following format: + Dictionary A Dictionary of the following format: - { - "format": integer, # for debugging purposes - "timestamp_naive": integer, # timestamp based on locally configured timezone - "timestamp_utc": integer # aware timestamp only if UTC timezone detected - } + { + "format": integer, # for debugging purposes. None if conversion fails + "timestamp_naive": integer, # timestamp based on locally configured timezone. None if conversion fails + "timestamp_utc": integer # aware timestamp only if UTC timezone detected. None if conversion fails + } - The format integer denotes which date_time format conversion succeeded. - The timestamp_naive integer is the converted date-time string to a naive epoch timestamp. - The timestamp_utc integer is the converted date-time string to an aware epoch timestamp - in the UTC timezone. If an aware conversion cannot be performed (e.g. the UTC timezone - is not found in the date-time string), then this field will be None. + The format integer denotes which date_time format conversion succeeded. + The timestamp_naive integer is the converted date-time string to a naive epoch timestamp. + The timestamp_utc integer is the converted date-time string to an aware epoch timestamp + in the UTC timezone. If an aware conversion cannot be performed (e.g. the UTC timezone + is not found in the date-time string), then this field will be None. - If the conversion completely fails, None is returned instead of a Dictionary + If the conversion completely fails, all fields will be None. diff --git a/jc/utils.py b/jc/utils.py index c6e3a34f..276f36ab 100644 --- a/jc/utils.py +++ b/jc/utils.py @@ -1,6 +1,6 @@ """jc - JSON CLI output utility utils""" -import textwrap import sys +import re import locale from datetime import datetime, timezone @@ -52,7 +52,7 @@ def compatibility(mod_name, compatible): Returns: - no return, just prints output to STDERR + None - just prints output to STDERR """ platform_found = False @@ -109,6 +109,7 @@ def parse_datetime_to_timestamp(data): If the conversion completely fails, all fields will be None. """ + normalized_datetime = '' utc_tz = False dt = None dt_utc = None @@ -128,6 +129,8 @@ def parse_datetime_to_timestamp(data): utc_tz = True else: utc_tz = False + elif '+0000' in data or '-0000' in data: + utc_tz = True formats = [ {'id': 1000, 'format': '%c', 'locale': None}, # C locale format conversion, or date cli command in C locale with non-UTC tz: Tue Mar 23 16:12:11 2021 or Tue Mar 23 16:12:11 IST 2021 @@ -137,6 +140,8 @@ def parse_datetime_to_timestamp(data): {'id': 5000, 'format': '%A %d %B %Y %I:%M:%S %p', 'locale': None}, # European-style local format with non-UTC tz (found in upower cli output): Tuesday 01 October 2019 12:50:41 PM IST {'id': 6000, 'format': '%a %b %d %I:%M:%S %p %Z %Y', 'locale': None}, # en_US.UTF-8 format (found in date cli): Wed Mar 24 06:16:19 PM UTC 2021 {'id': 7000, 'format': '%a %b %d %H:%M:%S %Z %Y', 'locale': None}, # C locale format (found in date cli): Wed Mar 24 11:11:30 UTC 2021 + {'id': 7100, 'format': '%b %d %X %Y', 'locale': None}, # C locale format (found in stat cli output - osx): # Mar 29 11:49:05 2021 + {'id': 7200, 'format': '%Y-%m-%d %X.%f %z', 'locale': None}, # C locale format (found in stat cli output - linux): 2019-08-13 18:13:43.555604315 -0400 # attempt locale changes last {'id': 8000, 'format': '%a %d %b %Y %H:%M:%S %Z', 'locale': ''}, # current locale format (found in upower cli output): # mar. 23 mars 2021 23:12:11 UTC {'id': 8100, 'format': '%a %d %b %Y %H:%M:%S', 'locale': ''}, # current locale format with non-UTC tz (found in upower cli output): # mar. 23 mars 2021 19:12:11 EDT @@ -179,6 +184,10 @@ def parse_datetime_to_timestamp(data): normalized_datetime = ' '.join(normalized_datetime_list) + # normalize further by converting any greater-than 6-digit subsecond to 6-digits + p = re.compile(r'(\W\d\d:\d\d:\d\d\.\d{6})\d+\W') + normalized_datetime = p.sub(r'\g<1> ', normalized_datetime) + for fmt in formats: try: locale.setlocale(locale.LC_TIME, fmt['locale']) diff --git a/tests/test_utils.py b/tests/test_utils.py index 916b54b9..30de7c6e 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -21,7 +21,13 @@ class MyTests(unittest.TestCase): # date cli command in en_US.UTF-8 format 'Wed Mar 24 06:16:19 PM UTC 2021': {'format': 6000, 'timestamp_naive': 1616634979, 'timestamp_utc': 1616609779}, # date cli command in C locale format - 'Wed Mar 24 11:11:30 UTC 2021': {'format': 7000, 'timestamp_naive': 1616609490, 'timestamp_utc': 1616584290} + 'Wed Mar 24 11:11:30 UTC 2021': {'format': 7000, 'timestamp_naive': 1616609490, 'timestamp_utc': 1616584290}, + # C locale format (found in stat cli output - OSX) + 'Mar 29 11:49:05 2021': {'format': 7100, 'timestamp_naive': 1617043745, 'timestamp_utc': None}, + # C local format (found in stat cli output - linux) non-UTC tz + '2019-08-13 18:13:43.555604315 -0400': {'format': 7200, 'timestamp_naive': 1565745223, 'timestamp_utc': None}, + # C local format (found in stat cli output - linux) UTC + '2019-08-13 18:13:43.555604315 -0000': {'format': 7200, 'timestamp_naive': 1565745223, 'timestamp_utc': 1565720023} } for input_string, expected_output in datetime_map.items():