# -*- coding: utf-8 -*- """ Copyright (C) 2014-2016 bromix (plugin.video.youtube) Copyright (C) 2016-2018 plugin.video.youtube SPDX-License-Identifier: GPL-2.0-only See LICENSES/GPL-2.0-only for more information. """ import re import time from datetime import date, datetime, timedelta from datetime import time as dt_time from six import PY2 from six import text_type from ..exceptions import KodionException __RE_MATCH_TIME_ONLY__ = re.compile(r'^(?P[0-9]{2})([:]?(?P[0-9]{2})([:]?(?P[0-9]{2}))?)?$') __RE_MATCH_DATE_ONLY__ = re.compile(r'^(?P[0-9]{4})[-]?(?P[0-9]{2})[-]?(?P[0-9]{2})$') __RE_MATCH_DATETIME__ = re.compile(r'^(?P[0-9]{4})[-]?(?P[0-9]{2})[-]?(?P[0-9]{2})["T ](?P[0-9]{2})[:]?(?P[0-9]{2})[:]?(?P[0-9]{2})') __RE_MATCH_PERIOD__ = re.compile(r'P((?P\d+)Y)?((?P\d+)M)?((?P\d+)D)?(T((?P\d+)H)?((?P\d+)M)?((?P\d+)S)?)?') __RE_MATCH_ABBREVIATED__ = re.compile(r'(\w+), (?P\d+) (?P\w+) (?P\d+) (?P\d+):(?P\d+):(?P\d+)') now = datetime.now def py2_utf8(text): result = text if PY2 and isinstance(result, text_type): return result.encode('utf-8', 'ignore') return result def parse(datetime_string, localize=True): _utc_to_local = utc_to_local if localize else lambda x: x def _to_int(value): if value is None: return 0 return int(value) # match time only '00:45:10' time_only_match = __RE_MATCH_TIME_ONLY__.match(datetime_string) if time_only_match: return _utc_to_local(datetime.combine(date.today(), dt_time(hour=_to_int(time_only_match.group('hour')), minute=_to_int(time_only_match.group('minute')), second=_to_int(time_only_match.group('second')))) ).time() # match date only '2014-11-08' date_only_match = __RE_MATCH_DATE_ONLY__.match(datetime_string) if date_only_match: return _utc_to_local(date(_to_int(date_only_match.group('year')), _to_int(date_only_match.group('month')), _to_int(date_only_match.group('day')))) # full date time date_time_match = __RE_MATCH_DATETIME__.match(datetime_string) if date_time_match: return _utc_to_local(datetime(_to_int(date_time_match.group('year')), _to_int(date_time_match.group('month')), _to_int(date_time_match.group('day')), _to_int(date_time_match.group('hour')), _to_int(date_time_match.group('minute')), _to_int(date_time_match.group('second')))) # period - at the moment we support only hours, minutes and seconds (e.g. videos and audio) period_match = __RE_MATCH_PERIOD__.match(datetime_string) if period_match: return timedelta(hours=_to_int(period_match.group('hours')), minutes=_to_int(period_match.group('minutes')), seconds=_to_int(period_match.group('seconds'))) # abbreviated match abbreviated_match = __RE_MATCH_ABBREVIATED__.match(datetime_string) if abbreviated_match: month = {'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4, 'May': 5, 'June': 6, 'Jun': 6, 'July': 7, 'Jul': 7, 'Aug': 8, 'Sept': 9, 'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12} return _utc_to_local(datetime(year=_to_int(abbreviated_match.group('year')), month=month[abbreviated_match.group('month')], day=_to_int(abbreviated_match.group('day')), hour=_to_int(abbreviated_match.group('hour')), minute=_to_int(abbreviated_match.group('minute')), second=_to_int(abbreviated_match.group('second')))) raise KodionException("Could not parse iso 8601 timestamp '%s'" % datetime_string) def get_scheduled_start(datetime_object, localize=True): start_hour = '{:02d}'.format(datetime_object.hour) start_minute = '{:<02d}'.format(datetime_object.minute) start_time = ':'.join([start_hour, start_minute]) start_date = str(datetime_object.date()) if localize: now = datetime.now() else: now = datetime.utcnow() start_date = start_date.replace(str(now.year), '').lstrip('-') start_date = start_date.replace('-'.join(['{:02d}'.format(now.month), '{:02d}'.format(now.day)]), '') return start_date, start_time local_timezone_offset = None def utc_to_local(dt): global local_timezone_offset if local_timezone_offset is None: now = time.time() local_timezone_offset = datetime.fromtimestamp(now) - datetime.utcfromtimestamp(now) return dt + local_timezone_offset def datetime_to_since(context, dt): now = datetime.now() diff = now - dt yesterday = now - timedelta(days=1) yyesterday = now - timedelta(days=2) use_yesterday = total_seconds(now - yesterday) > 10800 today = now.date() tomorrow = today + timedelta(days=1) seconds = total_seconds(diff) if seconds > 0: if seconds < 60: return py2_utf8(context.localize('30676')) elif 60 <= seconds < 120: return py2_utf8(context.localize('30677')) elif 120 <= seconds < 3600: return py2_utf8(context.localize('30678')) elif 3600 <= seconds < 7200: return py2_utf8(context.localize('30679')) elif 7200 <= seconds < 10800: return py2_utf8(context.localize('30680')) elif 10800 <= seconds < 14400: return py2_utf8(context.localize('30681')) elif use_yesterday and dt.date() == yesterday.date(): return ' '.join([py2_utf8(context.localize('30682')), context.format_time(dt)]) elif dt.date() == yyesterday.date(): return py2_utf8(context.localize('30683')) elif 5400 <= seconds < 86400: return ' '.join([py2_utf8(context.localize('30684')), context.format_time(dt)]) elif 86400 <= seconds < 172800: return ' '.join([py2_utf8(context.localize('30682')), context.format_time(dt)]) else: seconds *= -1 if seconds < 60: return py2_utf8(context.localize('30691')) elif 60 <= seconds < 120: return py2_utf8(context.localize('30692')) elif 120 <= seconds < 3600: return py2_utf8(context.localize('30693')) elif 3600 <= seconds < 7200: return py2_utf8(context.localize('30694')) elif 7200 <= seconds < 10800: return py2_utf8(context.localize('30695')) elif dt.date() == today: return ' '.join([py2_utf8(context.localize('30696')), context.format_time(dt)]) elif dt.date() == tomorrow: return ' '.join([py2_utf8(context.localize('30697')), context.format_time(dt)]) return ' '.join([context.format_date_short(dt), context.format_time(dt)]) def strptime(s, fmt='%Y-%m-%dT%H:%M:%S.%fZ'): # noinspection PyUnresolvedReferences ms_precision = '.' in s[-5:-1] if fmt == '%Y-%m-%dT%H:%M:%S.%fZ' and not ms_precision: fmt = '%Y-%m-%dT%H:%M:%SZ' elif fmt == '%Y-%m-%dT%H:%M:%SZ' and ms_precision: fmt = '%Y-%m-%dT%H:%M:%S.%fZ' import _strptime try: time.strptime('01 01 2012', '%d %m %Y') # dummy call except: pass return datetime(*time.strptime(s, fmt)[:6]) def total_seconds(t_delta): # required for python 2.6 which doesn't have datetime.timedelta.total_seconds return 24 * 60 * 60 * t_delta.days + t_delta.seconds + (t_delta.microseconds // 1000000.)