Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/python-nbxmpp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilipp Hörist <philipp@hoerist.com>2023-09-26 22:01:30 +0300
committerPhilipp Hörist <philipp@hoerist.com>2023-09-26 22:01:30 +0300
commitef32805a2c77b99440c49e7a352311d7eb2cb53a (patch)
tree712ca547ed104af12bbaa1bedd69acabafbae0bf
parent4ff3d8eccf8a934c74e44578b44c56a1c4b59542 (diff)
refactor: DateTime: Simplify parsing code
-rw-r--r--nbxmpp/modules/date_and_time.py124
-rw-r--r--test/unit/test_datetime_parsing.py9
2 files changed, 65 insertions, 68 deletions
diff --git a/nbxmpp/modules/date_and_time.py b/nbxmpp/modules/date_and_time.py
index b96df25..4815d28 100644
--- a/nbxmpp/modules/date_and_time.py
+++ b/nbxmpp/modules/date_and_time.py
@@ -25,24 +25,15 @@ from datetime import tzinfo
log = logging.getLogger('nbxmpp.m.date_and_time')
-PATTERN_DATETIME = re.compile(
- r'([0-9]{4}-[0-9]{2}-[0-9]{2})'
- r'T'
- r'([0-9]{2}:[0-9]{2}:[0-9]{2})'
- r'(\.[0-9]{0,6})?'
- r'(?:[0-9]+)?'
- r'(?:(Z)|(?:([-+][0-9]{2}):([0-9]{2})))$'
-)
-
-PATTERN_DELAY = re.compile(
- r'([0-9]{4}-[0-9]{2}-[0-9]{2})'
- r'T'
- r'([0-9]{2}:[0-9]{2}:[0-9]{2})'
- r'(\.[0-9]{0,6})?'
- r'(?:[0-9]+)?'
- r'(?:(Z)|(?:([-+][0]{2}):([0]{2})))$'
-)
-
+PATTERN_DATETIME = re.compile(r'''
+ ([0-9]{4}-[0-9]{2}-[0-9]{2}) # Date
+ T # Separator
+ ([0-9]{2}:[0-9]{2}:[0-9]{2}) # Time
+ (?P<frac>\.[0-9]{0,6})? # Fractual Seconds
+ [0-9]* # lose everything > 6
+ (Z|[-+][0-9]{2}:[0-9]{2}) # UTC Offset
+ $ # End of String
+''', re.VERBOSE)
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
@@ -124,8 +115,12 @@ def create_tzinfo(hours=0, minutes=0, tz_string=None):
return timezone(timedelta(hours=hours, minutes=minutes))
-def parse_datetime(timestring: str | None, check_utc=False,
- convert='utc', epoch=False):
+def parse_datetime(
+ timestring: str | None,
+ check_utc: bool = False,
+ convert: str | None = 'utc',
+ epoch: bool = False
+) -> datetime | float | None:
'''
Parse a XEP-0082 DateTime Profile String
@@ -149,52 +144,51 @@ def parse_datetime(timestring: str | None, check_utc=False,
return None
if convert not in (None, 'utc', 'local'):
raise TypeError('"%s" is not a valid value for convert')
+
+ match = PATTERN_DATETIME.match(timestring)
+ if match is None:
+ return
+
+ timestring = ''.join(match.groups(''))
+ strformat = '%Y-%m-%d%H:%M:%S%z'
+ if match.group('frac'):
+ # Fractional second addendum to Time
+ strformat = '%Y-%m-%d%H:%M:%S.%f%z'
+
+ try:
+ date_time = datetime.strptime(timestring, strformat)
+ except ValueError:
+ return None
+
if check_utc:
- match = PATTERN_DELAY.match(timestring)
- else:
- match = PATTERN_DATETIME.match(timestring)
-
- if match:
- timestring = ''.join(match.groups(''))
- strformat = '%Y-%m-%d%H:%M:%S%z'
- if match.group(3):
- # Fractional second addendum to Time
- strformat = '%Y-%m-%d%H:%M:%S.%f%z'
- if match.group(4):
- # UTC string denoted by addition of the character 'Z'
- timestring = timestring[:-1] + '+0000'
- try:
- date_time = datetime.strptime(timestring, strformat)
- except ValueError:
- pass
- else:
- if check_utc:
- if convert != 'utc':
- raise ValueError(
- 'check_utc can only be used with convert="utc"')
- date_time.replace(tzinfo=timezone.utc)
- if epoch:
- return date_time.timestamp()
- return date_time
-
- if convert == 'utc':
- date_time = date_time.astimezone(timezone.utc)
- if epoch:
- return date_time.timestamp()
- return date_time
-
- if epoch:
- # epoch is always UTC, use convert='utc' or check_utc=True
- raise ValueError(
- 'epoch not available while converting to local')
-
- if convert == 'local':
- date_time = date_time.astimezone(LocalTimezone())
- return date_time
-
- # convert=None
- return date_time
- return None
+ if convert != 'utc':
+ raise ValueError(
+ 'check_utc can only be used with convert="utc"')
+
+ if date_time.tzinfo != timezone.utc:
+ return None
+
+ if epoch:
+ return date_time.timestamp()
+ return date_time
+
+ if convert == 'utc':
+ date_time = date_time.astimezone(timezone.utc)
+ if epoch:
+ return date_time.timestamp()
+ return date_time
+
+ if epoch:
+ # epoch is always UTC, use convert='utc' or check_utc=True
+ raise ValueError(
+ 'epoch not available while converting to local')
+
+ if convert == 'local':
+ date_time = date_time.astimezone(LocalTimezone())
+ return date_time
+
+ # convert=None
+ return date_time
def get_local_time():
diff --git a/test/unit/test_datetime_parsing.py b/test/unit/test_datetime_parsing.py
index 7ef0aa5..8970611 100644
--- a/test/unit/test_datetime_parsing.py
+++ b/test/unit/test_datetime_parsing.py
@@ -33,8 +33,10 @@ class TestDateTime(unittest.TestCase):
strings2 = {
# Valid strings with offset
- '2017-11-05T01:41:20-05:00': datetime(2017, 11, 5, 1, 41, 20, 0, create_tzinfo(hours=-5)),
- '2017-11-05T01:41:20+05:00': datetime(2017, 11, 5, 1, 41, 20, 0, create_tzinfo(hours=5)),
+ '2017-11-05T07:41:20-05:00': datetime(2017, 11, 5, 12, 41, 20, 0, timezone.utc),
+ '2017-11-05T07:41:20+05:00': datetime(2017, 11, 5, 2, 41, 20, 0, timezone.utc),
+ '2017-11-05T01:41:20+00:00': datetime(2017, 11, 5, 1, 41, 20, 0, timezone.utc),
+ '2017-11-05T01:41:20Z': datetime(2017, 11, 5, 1, 41, 20, 0, timezone.utc),
}
for time_string, expected_value in strings.items():
@@ -43,7 +45,7 @@ class TestDateTime(unittest.TestCase):
for time_string, expected_value in strings2.items():
result = parse_datetime(time_string, convert='utc')
- self.assertEqual(result, expected_value.astimezone(timezone.utc))
+ self.assertEqual(result, expected_value)
def test_convert_to_local(self):
@@ -79,6 +81,7 @@ class TestDateTime(unittest.TestCase):
for time_string, expected_value in strings.items():
result = parse_datetime(time_string, convert=None)
+ assert result is not None
self.assertEqual(result.utcoffset(), expected_value)
def test_check_utc(self):