diff --git a/unfurl/parsers/parse_timestamp.py b/unfurl/parsers/parse_timestamp.py index 450af2d..bdc9fab 100644 --- a/unfurl/parsers/parse_timestamp.py +++ b/unfurl/parsers/parse_timestamp.py @@ -34,7 +34,11 @@ def trim_zero_fractional_seconds(timestamp_string, number_to_trim): incorrect precision to it. """ - if re.search(rf'\.\d{{{6 - number_to_trim}}}0{{{number_to_trim}}}$', timestamp_string): + m = re.search( + rf'\.\d{{{6 - number_to_trim}}}0{{{number_to_trim}}}(?P\+\d\d:\d\d)?$', timestamp_string) + if m and m.group('tz_offset'): + return f"{timestamp_string[:-(number_to_trim + 6)]}{m.group('tz_offset')}" + elif m: return timestamp_string[:-number_to_trim] return timestamp_string @@ -54,7 +58,7 @@ def decode_epoch_seconds(seconds): return { 'data_type': 'timestamp.epoch-seconds', 'display_type': 'Epoch seconds', - 'timestamp_value': str(datetime.datetime.fromtimestamp(float(seconds), datetime.UTC)) + 'timestamp_value': str(datetime.datetime.fromtimestamp(float(seconds), tz=datetime.UTC)) } @@ -73,7 +77,7 @@ def decode_epoch_centiseconds(centiseconds): """ # Trim off the 4 trailing 0s (don't add precision that wasn't in the timestamp) converted_ts = trim_zero_fractional_seconds( - str(datetime.datetime.fromtimestamp(float(centiseconds) / 100, datetime.UTC)), 4) + str(datetime.datetime.fromtimestamp(float(centiseconds) / 100, tz=datetime.UTC)), 4) return { 'data_type': 'timestamp.epoch-centiseconds', @@ -93,7 +97,7 @@ def decode_epoch_milliseconds(milliseconds): 2030: 1893456000000 """ - converted_dt = datetime.datetime(1970, 1, 1) + datetime.timedelta(milliseconds=float(milliseconds)) + converted_dt = datetime.datetime(1970, 1, 1, tzinfo=datetime.UTC) + datetime.timedelta(milliseconds=float(milliseconds)) # Trim off the 3 trailing 0s (don't add precision that wasn't in the timestamp) converted_ts = trim_zero_fractional_seconds(str(converted_dt), 3) @@ -119,7 +123,7 @@ def decode_epoch_ten_microseconds(ten_microseconds): """ # Trim off the trailing 0 (don't add precision that wasn't in the timestamp) converted_ts = trim_zero_fractional_seconds( - str(datetime.datetime.fromtimestamp(float(ten_microseconds) / 100000, datetime.UTC)), 1) + str(datetime.datetime.fromtimestamp(float(ten_microseconds) / 100000, tz=datetime.UTC)), 1) return { 'data_type': 'timestamp.epoch-ten-microseconds', @@ -139,7 +143,7 @@ def decode_epoch_microseconds(microseconds): 2030: 1893456000000000 """ - converted_ts = datetime.datetime.fromtimestamp(float(microseconds) / 1000000, datetime.UTC) + converted_ts = datetime.datetime.fromtimestamp(float(microseconds) / 1000000, tz=datetime.UTC) return { 'data_type': 'timestamp.epoch-microseconds', @@ -160,7 +164,7 @@ def decode_webkit(microseconds): 2030: 13537929600000000 """ - converted_ts = datetime.datetime.fromtimestamp((float(microseconds) / 1000000) - 11644473600, datetime.UTC) + converted_ts = datetime.datetime.fromtimestamp((float(microseconds) / 1000000) - 11644473600, tz=datetime.UTC) return { 'data_type': 'timestamp.webkit', @@ -182,7 +186,7 @@ def decode_windows_filetime(intervals): 2065: 146424672000000000 """ - converted_ts = datetime.datetime.fromtimestamp((float(intervals) / 10000000) - 11644473600, datetime.UTC) + converted_ts = datetime.datetime.fromtimestamp((float(intervals) / 10000000) - 11644473600, tz=datetime.UTC) return { 'data_type': 'timestamp.windows-filetime', @@ -211,7 +215,7 @@ def decode_datetime_ticks(ticks): """ seconds = (ticks - 621355968000000000) / 10000000 - converted_ts = datetime.datetime.fromtimestamp(seconds) + converted_ts = datetime.datetime.fromtimestamp(seconds, tz=datetime.UTC) return { 'data_type': 'timestamp.datetime-ticks', @@ -237,7 +241,7 @@ def decode_mac_absolute_time(seconds): 2035: 1072915200 """ - converted_ts = datetime.datetime.fromtimestamp(float(seconds) + 978307200, datetime.UTC) + converted_ts = datetime.datetime.fromtimestamp(float(seconds) + 978307200, tz=datetime.UTC) return { 'data_type': 'timestamp.mac-absolute-time', diff --git a/unfurl/tests/unit/test_linkedin.py b/unfurl/tests/unit/test_linkedin.py index 46da54b..28b4101 100644 --- a/unfurl/tests/unit/test_linkedin.py +++ b/unfurl/tests/unit/test_linkedin.py @@ -38,7 +38,7 @@ def test_linkedin_id(self): self.assertEqual(test.total_nodes, 13) # embedded timestamp parses correctly - self.assertEqual('2020-07-06 18:59:31.226', test.nodes[13].value) + self.assertIn('2020-07-06 18:59:31.226', test.nodes[13].value) # make sure the queue finished empty self.assertTrue(test.queue.empty()) diff --git a/unfurl/tests/unit/test_twitter.py b/unfurl/tests/unit/test_twitter.py index f80d003..404b645 100644 --- a/unfurl/tests/unit/test_twitter.py +++ b/unfurl/tests/unit/test_twitter.py @@ -21,7 +21,7 @@ def test_twitter(self): self.assertIn('Twitter Snowflakes', test.nodes[9].hover) # embedded timestamp parses correctly - self.assertEqual('2019-02-20 14:40:26.837', test.nodes[14].value) + self.assertIn('2019-02-20 14:40:26.837', test.nodes[14].value) # make sure the queue finished empty self.assertTrue(test.queue.empty())