Remove datetime parsing from Gajim
Use nbxmpp's datetime parsing
This commit is contained in:
parent
da46bdda1f
commit
348a8551fe
3 changed files with 2 additions and 295 deletions
|
@ -1,194 +0,0 @@
|
|||
# This file is part of Gajim.
|
||||
#
|
||||
# Gajim is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published
|
||||
# by the Free Software Foundation; version 3 only.
|
||||
#
|
||||
# Gajim is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# XEP-0082: XMPP Date and Time Profiles
|
||||
|
||||
import re
|
||||
import time
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from datetime import timezone
|
||||
from datetime import tzinfo
|
||||
|
||||
log = logging.getLogger('gajim.c.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})))$'
|
||||
)
|
||||
|
||||
|
||||
ZERO = timedelta(0)
|
||||
HOUR = timedelta(hours=1)
|
||||
SECOND = timedelta(seconds=1)
|
||||
|
||||
STDOFFSET = timedelta(seconds=-time.timezone)
|
||||
if time.daylight:
|
||||
DSTOFFSET = timedelta(seconds=-time.altzone)
|
||||
else:
|
||||
DSTOFFSET = STDOFFSET
|
||||
|
||||
DSTDIFF = DSTOFFSET - STDOFFSET
|
||||
|
||||
|
||||
class LocalTimezone(tzinfo):
|
||||
'''
|
||||
A class capturing the platform's idea of local time.
|
||||
May result in wrong values on historical times in
|
||||
timezones where UTC offset and/or the DST rules had
|
||||
changed in the past.
|
||||
'''
|
||||
def fromutc(self, dt):
|
||||
assert dt.tzinfo is self
|
||||
stamp = (dt - datetime(1970, 1, 1, tzinfo=self)) // SECOND
|
||||
args = time.localtime(stamp)[:6]
|
||||
dst_diff = DSTDIFF // SECOND
|
||||
# Detect fold
|
||||
fold = (args == time.localtime(stamp - dst_diff))
|
||||
return datetime(*args, microsecond=dt.microsecond,
|
||||
tzinfo=self, fold=fold)
|
||||
|
||||
def utcoffset(self, dt):
|
||||
if self._isdst(dt):
|
||||
return DSTOFFSET
|
||||
return STDOFFSET
|
||||
|
||||
def dst(self, dt):
|
||||
if self._isdst(dt):
|
||||
return DSTDIFF
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return 'local'
|
||||
|
||||
@staticmethod
|
||||
def _isdst(dt):
|
||||
tt = (dt.year, dt.month, dt.day,
|
||||
dt.hour, dt.minute, dt.second,
|
||||
dt.weekday(), 0, 0)
|
||||
stamp = time.mktime(tt)
|
||||
tt = time.localtime(stamp)
|
||||
return tt.tm_isdst > 0
|
||||
|
||||
|
||||
def create_tzinfo(hours=0, minutes=0, tz_string=None):
|
||||
if tz_string is None:
|
||||
return timezone(timedelta(hours=hours, minutes=minutes))
|
||||
|
||||
if tz_string.lower() == 'z':
|
||||
return timezone.utc
|
||||
|
||||
try:
|
||||
hours, minutes = map(int, tz_string.split(':'))
|
||||
except Exception:
|
||||
log.warning('Wrong tz string: %s', tz_string)
|
||||
return
|
||||
|
||||
if hours not in range(-24, 24):
|
||||
log.warning('Wrong tz string: %s', tz_string)
|
||||
return
|
||||
|
||||
if minutes not in range(0, 59):
|
||||
log.warning('Wrong tz string: %s', tz_string)
|
||||
return
|
||||
|
||||
if hours in (24, -24) and minutes != 0:
|
||||
log.warning('Wrong tz string: %s', tz_string)
|
||||
return
|
||||
return timezone(timedelta(hours=hours, minutes=minutes))
|
||||
|
||||
|
||||
def parse_datetime(timestring, check_utc=False,
|
||||
convert='utc', epoch=False):
|
||||
'''
|
||||
Parse a XEP-0082 DateTime Profile String
|
||||
|
||||
:param timestring: a XEP-0082 DateTime profile formated string
|
||||
|
||||
:param check_utc: if True, returns None if timestring is not
|
||||
a timestring expressing UTC
|
||||
|
||||
:param convert: convert the given timestring to utc or local time
|
||||
|
||||
:param epoch: if True, returns the time in epoch
|
||||
|
||||
Examples:
|
||||
'2017-11-05T01:41:20Z'
|
||||
'2017-11-05T01:41:20.123Z'
|
||||
'2017-11-05T01:41:20.123+05:00'
|
||||
|
||||
return a datetime or epoch
|
||||
'''
|
||||
if convert not in (None, 'utc', 'local'):
|
||||
raise TypeError('"%s" is not a valid value for convert')
|
||||
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
|
|
@ -18,12 +18,12 @@ import time
|
|||
|
||||
import nbxmpp
|
||||
from nbxmpp.structs import StanzaHandler
|
||||
from nbxmpp.modules.date_and_time import parse_datetime
|
||||
from nbxmpp.modules.date_and_time import create_tzinfo
|
||||
|
||||
from gajim.common import app
|
||||
from gajim.common.nec import NetworkEvent
|
||||
from gajim.common.modules.base import BaseModule
|
||||
from gajim.common.modules.date_and_time import parse_datetime
|
||||
from gajim.common.modules.date_and_time import create_tzinfo
|
||||
|
||||
|
||||
class EntityTime(BaseModule):
|
||||
|
|
|
@ -1,99 +0,0 @@
|
|||
import unittest
|
||||
from datetime import datetime
|
||||
from datetime import timezone
|
||||
from datetime import timedelta
|
||||
|
||||
from gajim.common.modules.date_and_time import parse_datetime
|
||||
from gajim.common.modules.date_and_time import LocalTimezone
|
||||
from gajim.common.modules.date_and_time import create_tzinfo
|
||||
|
||||
|
||||
class TestDateTime(unittest.TestCase):
|
||||
|
||||
def test_convert_to_utc(self):
|
||||
|
||||
strings = {
|
||||
# Valid UTC strings and fractions
|
||||
'2017-11-05T01:41:20Z': 1509846080.0,
|
||||
'2017-11-05T01:41:20.123Z': 1509846080.123,
|
||||
'2017-11-05T01:41:20.123123123+00:00': 1509846080.123123,
|
||||
'2017-11-05T01:41:20.123123123123123-00:00': 1509846080.123123,
|
||||
|
||||
# Invalid strings
|
||||
'2017-11-05T01:41:20Z+05:00': None,
|
||||
'2017-11-05T01:41:20+0000': None,
|
||||
'2017-11-05T01:41:20-0000': None,
|
||||
|
||||
# Valid strings with offset
|
||||
'2017-11-05T01:41:20-05:00': 1509864080.0,
|
||||
'2017-11-05T01:41:20+05:00': 1509828080.0,
|
||||
}
|
||||
|
||||
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)),
|
||||
}
|
||||
|
||||
for time_string, expected_value in strings.items():
|
||||
result = parse_datetime(time_string, convert='utc', epoch=True)
|
||||
self.assertEqual(result, expected_value)
|
||||
|
||||
for time_string, expected_value in strings2.items():
|
||||
result = parse_datetime(time_string, convert='utc')
|
||||
self.assertEqual(result, expected_value.astimezone(timezone.utc))
|
||||
|
||||
def test_convert_to_local(self):
|
||||
|
||||
strings = {
|
||||
# Valid UTC strings and fractions
|
||||
'2017-11-05T01:41:20Z': datetime(2017, 11, 5, 1, 41, 20, 0, timezone.utc),
|
||||
'2017-11-05T01:41:20.123Z': datetime(2017, 11, 5, 1, 41, 20, 123000, timezone.utc),
|
||||
'2017-11-05T01:41:20.123123123+00:00': datetime(2017, 11, 5, 1, 41, 20, 123123, timezone.utc),
|
||||
'2017-11-05T01:41:20.123123123123123-00:00': datetime(2017, 11, 5, 1, 41, 20, 123123, timezone.utc),
|
||||
|
||||
# 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)),
|
||||
}
|
||||
|
||||
for time_string, expected_value in strings.items():
|
||||
result = parse_datetime(time_string, convert='local')
|
||||
self.assertEqual(result, expected_value.astimezone(LocalTimezone()))
|
||||
|
||||
def test_no_convert(self):
|
||||
|
||||
strings = {
|
||||
# Valid UTC strings and fractions
|
||||
'2017-11-05T01:41:20Z': timedelta(0),
|
||||
'2017-11-05T01:41:20.123Z': timedelta(0),
|
||||
'2017-11-05T01:41:20.123123123+00:00': timedelta(0),
|
||||
'2017-11-05T01:41:20.123123123123123-00:00': timedelta(0),
|
||||
|
||||
# Valid strings with offset
|
||||
'2017-11-05T01:41:20-05:00': timedelta(hours=-5),
|
||||
'2017-11-05T01:41:20+05:00': timedelta(hours=5),
|
||||
}
|
||||
|
||||
for time_string, expected_value in strings.items():
|
||||
result = parse_datetime(time_string, convert=None)
|
||||
self.assertEqual(result.utcoffset(), expected_value)
|
||||
|
||||
def test_check_utc(self):
|
||||
|
||||
strings = {
|
||||
# Valid UTC strings and fractions
|
||||
'2017-11-05T01:41:20Z': 1509846080.0,
|
||||
'2017-11-05T01:41:20.123Z': 1509846080.123,
|
||||
'2017-11-05T01:41:20.123123123+00:00': 1509846080.123123,
|
||||
'2017-11-05T01:41:20.123123123123123-00:00': 1509846080.123123,
|
||||
|
||||
# Valid strings with offset
|
||||
'2017-11-05T01:41:20-05:00': None,
|
||||
'2017-11-05T01:41:20+05:00': None,
|
||||
}
|
||||
|
||||
for time_string, expected_value in strings.items():
|
||||
result = parse_datetime(
|
||||
time_string, check_utc=True, epoch=True)
|
||||
self.assertEqual(result, expected_value)
|
Loading…
Add table
Reference in a new issue