Source code for pytimeparser.api

# -*- coding: utf-8 -*-


import re
import random
import datetime

from .utils import (
    normalize_text, expand_regex,
    mapping_inversed, mapping_to_list,
)


TIME_UNIT_MAPPING = dict(
    years=['y', 'year', 'years'],
    months=['month', 'months'],
    weeks=['w', 'wk', 'wks', 'week', 'weeks'],
    days=['d', 'day', 'days'],
    hours=['h', 'hr', 'hrs', 'hour', 'hours'],
    minutes=['m', 'min', 'mins', 'minute', 'minutes'],
    seconds=['s', 'sec', 'secs', 'second', 'seconds'],
    milliseconds=['ms', 'milli', 'millis', 'millisecond', 'milliseconds'],
    microseconds=['microsecond', 'microseconds'],
)

TIME_UNIT_INVERSED_MAPPING = mapping_inversed(TIME_UNIT_MAPPING)

TIME_UNIT_VARIATIONS = mapping_to_list(TIME_UNIT_INVERSED_MAPPING)

REGEX_SUPPORTED_TIME_UNIT_VARIATIONS = re.compile(
    r'|'.join([
        r'\b{}\b'.format(each)
        for each in TIME_UNIT_VARIATIONS
    ]),
    flags=re.UNICODE | re.IGNORECASE
)

REGEX_TIME_UNIT = re.compile(
    r'((?P<value_from>-?\d+(\.\d+)?)'
    r'(\s*to\s*(?P<value_to>-?\d+(\.\d+)?))?\s*)?'
    r'(?P<unit>{})'
    .format(REGEX_SUPPORTED_TIME_UNIT_VARIATIONS.pattern),
    flags=re.UNICODE | re.IGNORECASE
)

REGEX_DURATION = re.compile(
    r'((?P<hours>-?\d+(\.\d+)?)\s*:\s*(?P<minutes>-?\d+(\.\d+)?)'
    r'(\s*:\s*(?P<seconds>-?\d+(\.\d+)?)'
    r'(\s*:\s*(?P<milliseconds>-?\d+(\.\d+)?)'
    r'(\s*\.\s*(?P<microseconds>-?\d+(\.\d+)?))?)?)?)',
    flags=re.UNICODE | re.IGNORECASE
)

REGEX_ISO8601_DURATION = re.compile(
    r'(\s*P\s*'
    r'(\s*(?P<years>-?\d+(\.\d+)?)\s*Y\s*)?'
    r'(\s*(?P<months>-?\d+(\.\d+)?)\s*M\s*)?'
    r'(\s*(?P<weeks>-?\d+(\.\d+)?)\s*W\s*)?'
    r'(\s*(?P<days>-?\d+(\.\d+)?)\s*D\s*)?'
    r'(\s*T\s*'
    r'(\s*(?P<hours>-?\d+(\.\d+)?)\s*H\s*)?'
    r'(\s*(?P<minutes>-?\d+(\.\d+)?)\s*M\s*)?'
    r'(\s*(?P<seconds>-?\d+(\.\d+)?)\s*S\s*)?'
    r')?'
    r')',
    flags=re.UNICODE | re.IGNORECASE
)


[docs]def parse(text): """Parse time expression. :param text: Time expression to parse :return: :class:`datetime.timedelta` object :rtype: datetime.timedelta :raises: TypeError: if text is not a string :raises: ValueError: if text is empty """ if not isinstance(text, ("".__class__, u"".__class__)): raise TypeError('Expected a string, received: %s' % type(text).__name__) text = normalize_text(text) if not text: raise ValueError('Expected a time expression, recieved an empty string') duration_regexs = (REGEX_DURATION, REGEX_ISO8601_DURATION,) for duration_regex in duration_regexs: text = expand_regex(duration_regex, text, '{value} {name}') kwargs = {} for match in REGEX_TIME_UNIT.finditer(text): unit = match.group('unit').lower() value_from = float(match.group('value_from') or '1') if match.group('value_to'): value_to = float(match.group('value_to')) value = random.uniform(value_from, value_to) else: value = value_from unit = TIME_UNIT_INVERSED_MAPPING[unit].lower() if unit == 'years': unit, value = 'weeks', value * 52 elif unit == 'months': unit, value = 'weeks', value * 4 elif unit == 'microseconds': unit, value = 'milliseconds', value * 0.001 kwargs[unit] = kwargs.get(unit, 0) + value return datetime.timedelta(**kwargs)