Add a new XEP-0082 parsing method
This handles every possible XEP-0082 timestamp It has some additional options: - Check if a timestring is a valid UTC timestamp, as required by some XEPs (for example: XEP-0203) - Return timestamp as datetime in UTC - Return timestamp as datetime in localtime - Return timestamp as epoch
This commit is contained in:
parent
c1decf682b
commit
0fee27928d
|
@ -43,7 +43,7 @@ import shlex
|
|||
from common import caps_cache
|
||||
import socket
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone, tzinfo
|
||||
|
||||
from encodings.punycode import punycode_encode
|
||||
from string import Template
|
||||
|
@ -81,6 +81,78 @@ log = logging.getLogger('gajim.c.helpers')
|
|||
|
||||
special_groups = (_('Transports'), _('Not in Roster'), _('Observers'), _('Groupchats'))
|
||||
|
||||
# Patterns for DateTime parsing XEP-0082
|
||||
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
|
||||
else:
|
||||
return STDOFFSET
|
||||
|
||||
def dst(self, dt):
|
||||
if self._isdst(dt):
|
||||
return DSTDIFF
|
||||
else:
|
||||
return ZERO
|
||||
|
||||
def tzname(self, dt):
|
||||
return 'local'
|
||||
|
||||
def _isdst(self, 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
|
||||
|
||||
class InvalidFormat(Exception):
|
||||
pass
|
||||
|
||||
|
@ -629,6 +701,56 @@ def parse_delay(timestamp):
|
|||
raise
|
||||
return datetime_.timestamp()
|
||||
|
||||
def parse_datetime(timestring, check_utc=False, convert='utc', epoch=False):
|
||||
'''
|
||||
Parse a XEP-0082 DateTime Profile String
|
||||
https://xmpp.org/extensions/xep-0082.html
|
||||
|
||||
: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 not check_utc and convert == 'utc':
|
||||
date_time = date_time.astimezone(timezone.utc)
|
||||
if convert == 'local':
|
||||
date_time = date_time.astimezone(LocalTimezone())
|
||||
if epoch:
|
||||
return date_time.timestamp()
|
||||
return date_time
|
||||
return None
|
||||
|
||||
from common import gajim
|
||||
if gajim.HAVE_PYCURL:
|
||||
|
|
Loading…
Reference in New Issue