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
					
				
					 1 changed files with 123 additions and 1 deletions
				
			
		| 
						 | 
				
			
			@ -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…
	
	Add table
		
		Reference in a new issue