Exemple #1
0
def e161(number, alphabet=PHONE_E161_ALPHABET):
    '''
    Printable a 26 Latin letters (A to Z) phone number to the 12-key telephone
    keypad number.

    :param number: string
    :param alphabet: dict

    >>> e161('0800-PIZZA123')
    u'080074992123'
    >>> e161('0800^PIZZA123')
    Traceback (most recent call last):
        ...
    ValueError: Character "^" (0x5e) is not in the E.161 alphabet
    '''

    digits = []
    for char in strip(number, '+-. ()').lower():
        length = len(digits)
        for group, digit in alphabet.iteritems():
            if char in group:
                digits.append(digit)
                break

        if len(digits) == length:
            raise ValueError(
                _('Character "%s" (0x%02x) is not in the E.161 alphabet') %
                (char, ord(char)))

    return u''.join(digits)
Exemple #2
0
def e161(number, alphabet=PHONE_E161_ALPHABET):
    '''
    Printable a 26 Latin letters (A to Z) phone number to the 12-key telephone
    keypad number.

    :param number: string
    :param alphabet: dict

    >>> print(e161('0800-PIZZA123'))
    080074992123
    >>> e161('0800^PIZZA123')
    Traceback (most recent call last):
        ...
    ValueError: Character "^" (0x5e) is not in the E.161 alphabet
    '''

    digits = []
    for char in strip(number, '+-. ()').lower():
        length = len(digits)
        for group, digit in alphabet.items():
            if char in group:
                digits.append(digit)
                break

        if len(digits) == length:
            raise ValueError(
                _('Character "%s" (0x%02x) is not in the E.161 alphabet') %
                (char, ord(char))
            )

    return u''.join(digits)
Exemple #3
0
def day(t, now=None, format="%B %d"):
    """
    Date delta compared to ``t``. You can override ``now`` to specify what date
    to compare to.

    You can override the date format by supplying a ``format`` parameter.

    :param t: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
              object
    :param now: default ``None``, optionally a :class:`datetime.datetime`
                object
    :param format: default ``'%B %d'``

    >>> import time
    >>> day(time.time())
    'today'
    >>> day(time.time() - 86400)
    'yesterday'
    >>> day(time.time() - 604800)
    'last week'
    >>> day(time.time() + 86400)
    'tomorrow'
    >>> day(time.time() + 604800)
    'next week'
    """
    t1 = _to_date(t)
    t2 = _to_date(now or datetime.datetime.now())
    diff = t1 - t2
    secs = _total_seconds(diff)
    days = abs(diff.days)

    if days == 0:
        return _("today")
    elif days == 1:
        if secs < 0:
            return _("yesterday")
        else:
            return _("tomorrow")
    elif days == 7:
        if secs < 0:
            return _("last week")
        else:
            return _("next week")
    else:
        return t1.strftime(format)
Exemple #4
0
def day(t, now=None, format='%B %d'):
    '''
    Date delta compared to ``t``. You can override ``now`` to specify what date
    to compare to.

    You can override the date format by supplying a ``format`` parameter.

    :param t: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
              object
    :param now: default ``None``, optionally a :class:`datetime.datetime`
                object
    :param format: default ``'%B %d'``

    >>> import time
    >>> print(day(time.time()))
    today
    >>> print(day(time.time() - 86400))
    yesterday
    >>> print(day(time.time() - 604800))
    last week
    >>> print(day(time.time() + 86400))
    tomorrow
    >>> print(day(time.time() + 604800))
    next week
    '''
    t1 = _to_date(t)
    t2 = _to_date(now or datetime.datetime.now())
    diff = t1 - t2
    secs = _total_seconds(diff)
    days = abs(diff.days)

    if days == 0:
        return _('today')
    elif days == 1:
        if secs < 0:
            return _('yesterday')
        else:
            return _('tomorrow')
    elif days == 7:
        if secs < 0:
            return _('last week')
        else:
            return _('next week')
    else:
        return t1.strftime(format)
Exemple #5
0
def meid(number, separator=u' '):
    '''
    Printable Mobile Equipment Identifier (MEID) number.

    >>> meid(123456789012345678)
    u'1B 69B4BA 630F34 6'
    >>> meid('1B69B4BA630F34')
    u'1B 69B4BA 630F34 6'
    '''

    if isinstance(number, six.string_types):
        number = re.sub(r'[\s-]', '', number)

        try:
            number = '%014X' % int(number, 16)
        except ValueError:
            if len(number) < 18 and number.isdigit():
                return meid('%014X' % int(number), separator)
            else:
                raise ValueError(_('Invalid MEID, size mismatch'))
        else:
            if len(number) not in (14, 15):
                raise ValueError(_('Invalid MEID, size mismatch'))

    elif isinstance(number, six.integer_types):
        if number > 0xfffffffffffffff:
            raise ValueError(_('Invalid MEID, size mismatch'))
        return meid(('%014X' % number)[:14], separator)

    else:
        raise TypeError(_('Invalid MEID, input type invalid'))

    number = number.upper()
    region = number[:2]
    manufacturer = number[2:8]
    serial_number = number[8:14]
    check_digit = number[14:]

    if check_digit == '':
        check_digit = luhn_calc(number, chars='0123456789ABCDEF')

    groups = (region, manufacturer, serial_number, check_digit)
    return separator.join(list(filter(None, groups)))
Exemple #6
0
def meid(number, separator=u' '):
    '''
    Printable Mobile Equipment Identifier (MEID) number.

    >>> print(meid(123456789012345678))
    1B 69B4BA 630F34 6
    >>> print(meid('1B69B4BA630F34'))
    1B 69B4BA 630F34 6
    '''

    if isinstance(number, six.string_types):
        number = re.sub(r'[\s-]', '', number)

        try:
            number = '%014X' % int(number, 16)
        except ValueError:
            if len(number) < 18 and number.isdigit():
                return meid('%014X' % int(number), separator)
            else:
                raise ValueError(_('Invalid MEID, size mismatch'))
        else:
            if len(number) not in (14, 15):
                raise ValueError(_('Invalid MEID, size mismatch'))

    elif isinstance(number, six.integer_types):
        if number > 0xfffffffffffffff:
                raise ValueError(_('Invalid MEID, size mismatch'))
        return meid(('%014X' % number)[:14], separator)

    else:
        raise TypeError(_('Invalid MEID, input type invalid'))

    number = number.upper()
    region = number[:2]
    manufacturer = number[2:8]
    serial_number = number[8:14]
    check_digit = number[14:]

    if check_digit == '':
        check_digit = luhn_calc(number, chars='0123456789ABCDEF')

    groups = (region, manufacturer, serial_number, check_digit)
    return separator.join(list(filter(None, groups)))
Exemple #7
0
def e123(number, areasize=3, groupsize=4, national=False):
    '''
    Printable E.123 (Notation for national and international telephone numbers
    from ITU) numbers.

    :param number: string
    :param areasize: int
    :param groupsize: int
    :param national: bool

    >>> print(e123(155542315678))
    +1 555 4231 5678
    >>> print(e123('+31654231567', areasize=1))
    +31 6 5423 1567
    >>> print(e123('+3114020', areasize=2))
    +31 14 020
    >>> print(e123('+312054231567', areasize=2, national=True))
    (020) 5423 1567
    '''

    if isinstance(number, six.integer_types):
        return e123('+%s' % number, areasize, groupsize)

    elif isinstance(number, six.string_types):
        number = strip(number, '-. ()')
        if number.startswith('+'):
            number = number[1:]

        if not number.isdigit():
            raise ValueError(_('Invalid telephone number'))

        groups = []
        prefix = ''
        remain = number

        if national:
            for x in six.moves.xrange(3, 0, -1):
                if number[:x] in PHONE_PREFIX:
                    groups.append('(0%s)' % number[x:x + areasize])
                    remain = number[x + areasize:]
                    break

        else:
            prefix = '+'
            for x in six.moves.xrange(3, 0, -1):
                if number[:x] in PHONE_PREFIX:
                    groups.append(number[:x])
                    groups.append(number[x:x + areasize])
                    remain = number[x + areasize:]
                    break

        for x in six.moves.xrange(0, len(remain) + 1, groupsize):
            groups.append(remain[x:x + groupsize])

        return '%s%s' % (prefix, ' '.join(list(filter(None, groups))))
Exemple #8
0
def throughput(sample, window=1, format='decimal'):
    '''
    Return the throughput in (intelli)bytes per second.

    :param sample: number of samples sent
    :param window: default 1, sample window in seconds or
                   :class:`datetime.timedelta` object
    :param format: default 'decimal', see :func:`natural.size.filesize`

    >>> throughput(123456, 42)
    u'2.87 kB/s'
    '''

    if isinstance(window, datetime.timedelta):
        window = float(window.days * 86400 + window.seconds)
    elif isinstance(window, basestring):
        window = float(window)

    per_second = sample / float(window)
    return _('%s/s') % (filesize(per_second, format=format),)
Exemple #9
0
def throughput(sample, window=1, format='decimal'):
    '''
    Return the throughput in (intelli)bytes per second.

    :param sample: number of samples sent
    :param window: default 1, sample window in seconds or
                   :class:`datetime.timedelta` object
    :param format: default 'decimal', see :func:`natural.size.filesize`

    >>> throughput(123456, 42)
    u'2.87 kB/s'
    '''

    if isinstance(window, datetime.timedelta):
        window = float(window.days * 86400 + window.seconds)
    elif isinstance(window, basestring):
        window = float(window)

    per_second = sample / float(window)
    return _('%s/s') % (filesize(per_second, format=format), )
Exemple #10
0
def _to_datetime(t):
    '''
    Internal function that tries whatever to convert ``t`` into a
    :class:`datetime.datetime` object.


    >>> _to_datetime('2013-12-11')
    datetime.datetime(2013, 12, 11, 0, 0)
    >>> _to_datetime('Wed, 11 Dec 2013')
    datetime.datetime(2013, 12, 11, 0, 0)
    >>> _to_datetime('Wed, 11 Dec 13')
    datetime.datetime(2013, 12, 11, 0, 0)
    >>> _to_datetime('2012-06-13T15:24:17')
    datetime.datetime(2012, 6, 13, 15, 24, 17)

    '''

    if isinstance(t, six.integer_types + (float, )):
        return datetime.datetime.fromtimestamp(t).replace(microsecond=0)

    elif isinstance(t, six.string_types):
        for date_format in ALL_DATETIME_FORMATS:
            try:
                d = datetime.datetime.strptime(t, date_format)
                return d.replace(microsecond=0)
            except ValueError:
                pass

        raise ValueError(_('Format "%s" not supported') % t)

    elif isinstance(t, datetime.datetime):
        return t.replace(microsecond=0)

    elif isinstance(t, datetime.date):
        d = datetime.datetime.combine(t, datetime.time(0, 0))
        return d.replace(microsecond=0)

    else:
        raise TypeError
Exemple #11
0
def _to_datetime(t):
    """
    Internal function that tries whatever to convert ``t`` into a
    :class:`datetime.datetime` object.


    >>> _to_datetime('2013-12-11')
    datetime.datetime(2013, 12, 11, 0, 0)
    >>> _to_datetime('Wed, 11 Dec 2013')
    datetime.datetime(2013, 12, 11, 0, 0)
    >>> _to_datetime('Wed, 11 Dec 13')
    datetime.datetime(2013, 12, 11, 0, 0)
    >>> _to_datetime('2012-06-13T15:24:17')
    datetime.datetime(2012, 6, 13, 15, 24, 17)

    """

    if isinstance(t, six.integer_types + (float,)):
        return datetime.datetime.fromtimestamp(t).replace(microsecond=0)

    elif isinstance(t, six.string_types):
        for date_format in ALL_DATETIME_FORMATS:
            try:
                d = datetime.datetime.strptime(t, date_format)
                return d.replace(microsecond=0)
            except ValueError:
                pass

        raise ValueError(_('Format "%s" not supported') % t)

    elif isinstance(t, datetime.datetime):
        return t.replace(microsecond=0)

    elif isinstance(t, datetime.date):
        d = datetime.datetime.combine(t, datetime.time(0, 0))
        return d.replace(microsecond=0)

    else:
        raise TypeError
Exemple #12
0
def imei(number):
    '''
    Printable International Mobile Station Equipment Identity (IMEI) numbers.

    :param number: string, int or long

    >>> print imei(12345678901234)
    12-345678-901234-7
    >>> print imei(1234567890123456)
    12-345678-901234-56
    '''
    number = to_decimal(number)
    length = len(number)
    if length not in (14, 15, 16):
        raise ValueError(
            _('Invaid International Mobile Station Equipment Identity'))

    if len(number) == 14:
        # Add Luhn check digit
        number = luhn_append(number)

    groups = (number[:2], number[2:8], number[8:14], number[14:])
    return u'-'.join(list(filter(None, groups)))
Exemple #13
0
def imei(number):
    '''
    Printable International Mobile Station Equipment Identity (IMEI) numbers.

    :param number: string or int

    >>> print(imei(12345678901234))
    12-345678-901234-7
    >>> print(imei(1234567890123456))
    12-345678-901234-56
    '''
    number = to_decimal(number)
    length = len(number)
    if length not in (14, 15, 16):
        raise ValueError(
            _('Invaid International Mobile Station Equipment Identity')
        )

    if len(number) == 14:
        # Add Luhn check digit
        number = luhn_append(number)

    groups = (number[:2], number[2:8], number[8:14], number[14:])
    return u'-'.join(list(filter(None, groups)))
Exemple #14
0
def duration(t, now=None, precision=1, pad=', ', words=None,
             justnow=datetime.timedelta(seconds=10)):
    '''
    Time delta compared to ``t``. You can override ``now`` to specify what time
    to compare to.

    :param t: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
              object
    :param now: default ``None``, optionally a :class:`datetime.datetime`
                object
    :param precision: default ``1``, number of fragments to return
    :param words: default ``None``, allow words like "yesterday", if set to
                  ``None`` this will be enabled if ``precision`` is set to
                  ``1``
    :param justnow: default ``datetime.timedelta(seconds=10)``,
                    :class:`datetime.timedelta` object passed to :func:`delta`
                    representing tolerance for considering argument ``t`` as
                    meaning 'just now'

    >>> import time
    >>> from datetime import datetime
    >>> print(duration(time.time() + 1))
    just now
    >>> print(duration(time.time() + 11))
    11 seconds from now
    >>> print(duration(time.time() - 1))
    just now
    >>> print(duration(time.time() - 11))
    11 seconds ago
    >>> print(duration(time.time() - 3601))
    an hour ago
    >>> print(duration(time.time() - 7201))
    2 hours ago
    >>> print(duration(time.time() - 1234567))
    2 weeks ago
    >>> print(duration(time.time() + 7200, precision=1))
    2 hours from now
    >>> print(duration(time.time() - 1234567, precision=3))
    2 weeks, 6 hours, 56 minutes ago
    >>> print(duration(datetime(2014, 9, 8), now=datetime(2014, 9, 9)))
    yesterday
    >>> print(duration(datetime(2014, 9, 7, 23), now=datetime(2014, 9, 9)))
    1 day ago
    >>> print(duration(datetime(2014, 9, 10), now=datetime(2014, 9, 9)))
    tomorrow
    >>> print(duration(datetime(2014, 9, 11, 1), now=datetime(2014, 9, 9, 23)))
    1 day from now
    '''

    if words is None:
        words = precision == 1

    t1 = _to_datetime(t)
    t2 = _to_datetime(now or datetime.datetime.now())

    if t1 < t2:
        format = _('%s ago')
    else:
        format = _('%s from now')

    result, remains = delta(t1, t2, words=words, justnow=justnow)
    if result in (
            _('just now'),
            _('yesterday'),
            _('tomorrow'),
            _('last week'),
            _('next week'),
    ):
        return result

    elif precision > 1 and remains:
        t3 = t2 - datetime.timedelta(seconds=remains)
        return pad.join([
            result,
            duration(t2, t3, precision - 1, pad, words=False),
        ])

    else:
        return format % (result,)
Exemple #15
0
   Return the localised translation of ``message``, based on the current global
   domain, language, and locale directory.


.. py:function:: _multi(singular, plural, count)

   Return the localised translation based on ``count``, the ``singular``
   version if ``count == 1``, the ``plural`` version otherwise.

'''


# natural.bank
BBAN_RULES = dict(
    AD=dict(bban='4!n4!n12!c',
            name=_('Andorra')),
    AE=dict(bban='3!n16!n',
            name=_('United Arab Emirates')),
    AL=dict(bban='8!n16!c',
            name=_('Albania')),
    AT=dict(bban='5!n11!n',
            name=_('Austria')),
    AZ=dict(bban='4!a20!c',
            name=_('Republic of Azerbaijan')),
    BA=dict(bban='3!n3!n8!n2!n',
            name=_('Bosnia and Herzegovina')),
    BE=dict(bban='3!n7!n2!n',
            name=_('Belgium')),
    BG=dict(bban='4!a4!n2!n8!c',
            name=_('Bulgaria')),
    BH=dict(bban='4!a14!c',
Exemple #16
0
def compress(t, sign=False, pad=""):
    """
    Convert the input to compressed format, works with a
    :class:`datetime.timedelta` object or a number that represents the number
    of seconds you want to compress.  If you supply a timestamp or a
    :class:`datetime.datetime` object, it will give the delta relative to the
    current time.

    You can enable showing a sign in front of the compressed format with the
    ``sign`` parameter, the default is not to show signs.

    Optionally, you can chose to pad the output. If you wish your values to be
    separated by spaces, set ``pad`` to ``' '``.

    :param t: seconds or :class:`datetime.timedelta` object
    :param sign: default ``False``
    :param pad: default ``''``

    >>> compress(1)
    '1s'
    >>> compress(12)
    '12s'
    >>> compress(123)
    '2m3s'
    >>> compress(1234)
    '20m34s'
    >>> compress(12345)
    '3h25m45s'
    >>> compress(123456)
    '1d10h17m36s'

    """

    if isinstance(t, datetime.timedelta):
        seconds = t.seconds + (t.days * 86400)
    elif isinstance(t, six.integer_types + (float,)):
        return compress(datetime.timedelta(seconds=t), sign, pad)
    else:
        return compress(datetime.datetime.now() - _to_datetime(t), sign, pad)

    parts = []
    if sign:
        parts.append("-" if t.days < 0 else "+")

    weeks, seconds = divmod(seconds, TIME_WEEK)
    days, seconds = divmod(seconds, TIME_DAY)
    hours, seconds = divmod(seconds, TIME_HOUR)
    minutes, seconds = divmod(seconds, TIME_MINUTE)

    if weeks:
        parts.append(_("%dw") % (weeks,))
    if days:
        parts.append(_("%dd") % (days,))
    if hours:
        parts.append(_("%dh") % (hours,))
    if minutes:
        parts.append(_("%dm") % (minutes,))
    if seconds:
        parts.append(_("%ds") % (seconds,))

    return pad.join(parts)
Exemple #17
0
def duration(t, now=None, precision=1, pad=", ", words=None, justnow=datetime.timedelta(seconds=10)):
    """
    Time delta compared to ``t``. You can override ``now`` to specify what time
    to compare to.

    :param t: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
              object
    :param now: default ``None``, optionally a :class:`datetime.datetime`
                object
    :param precision: default ``1``, number of fragments to return
    :param words: default ``None``, allow words like "yesterday", if set to
                  ``None`` this will be enabled if ``precision`` is set to
                  ``1``
    :param justnow: default ``datetime.timedelta(seconds=10)``,
                    :class:`datetime.timedelta` object passed to :func:`delta`
                    representing tolerance for considering argument ``t`` as
                    meaning 'just now'

    >>> import time
    >>> from datetime import datetime
    >>> duration(time.time() + 1)
    'just now'
    >>> duration(time.time() + 11)
    '11 seconds from now'
    >>> duration(time.time() - 1)
    'just now'
    >>> duration(time.time() - 11)
    '11 seconds ago'
    >>> duration(time.time() - 3601)
    'an hour ago'
    >>> duration(time.time() - 7201)
    '2 hours ago'
    >>> duration(time.time() - 1234567)
    '2 weeks ago'
    >>> duration(time.time() + 7200, precision=1)
    '2 hours from now'
    >>> duration(time.time() - 1234567, precision=3)
    '2 weeks, 6 hours, 56 minutes ago'
    >>> duration(datetime(2014, 9, 8), now=datetime(2014, 9, 9))
    'yesterday'
    >>> duration(datetime(2014, 9, 7, 23), now=datetime(2014, 9, 9))
    '1 day ago'
    >>> duration(datetime(2014, 9, 10), now=datetime(2014, 9, 9))
    'tomorrow'
    >>> duration(datetime(2014, 9, 11, 1), now=datetime(2014, 9, 9, 23))
    '1 day from now'
    """

    if words is None:
        words = precision == 1

    t1 = _to_datetime(t)
    t2 = _to_datetime(now or datetime.datetime.now())

    if t1 < t2:
        format = _("%s ago")
    else:
        format = _("%s from now")

    result, remains = delta(t1, t2, words=words, justnow=justnow)
    if result in (_("just now"), _("yesterday"), _("tomorrow"), _("last week"), _("next week")):
        return result

    elif precision > 1 and remains:
        t3 = t2 - datetime.timedelta(seconds=remains)
        return pad.join([result, duration(t2, t3, precision - 1, pad, words=False)])

    else:
        return format % (result,)
Exemple #18
0
def delta(t1, t2, words=True, justnow=datetime.timedelta(seconds=10)):
    """
    Calculates the estimated delta between two time objects in human-readable
    format. Used internally by the :func:`day` and :func:`duration` functions.

    :param t1: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
               object
    :param t2: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
               object
    :param words: default ``True``, allow words like "yesterday", "tomorrow"
               and "just now"
    :param justnow: default ``datetime.timedelta(seconds=10)``,
               :class:`datetime.timedelta` object representing tolerance for
               considering a delta as meaning 'just now'

    >>> delta(_to_datetime('2012-06-13T15:24:17'), \
_to_datetime('2013-12-11T12:34:56'))
    ('77 weeks', -594639)
    """

    t1 = _to_datetime(t1)
    t2 = _to_datetime(t2)
    diff = t1 - t2
    date_diff = t1.date() - t2.date()

    # The datetime module includes milliseconds with float precision. Floats
    # will give unexpected results here, so we round the value here
    total = math.ceil(_total_seconds(diff))
    total_abs = abs(total)

    if total_abs < TIME_DAY:
        if abs(diff) < justnow and words:
            return (_("just now"), 0)

        elif total_abs < TIME_MINUTE:
            seconds = total_abs
            return (_multi(_("%d second"), _("%d seconds"), seconds) % (seconds,), 0)
        elif total_abs < TIME_MINUTE * 2 and words:
            return (_("a minute"), 0)

        elif total_abs < TIME_HOUR:
            minutes, seconds = divmod(total_abs, TIME_MINUTE)
            if total < 0:
                seconds *= -1
            return (_multi(_("%d minute"), _("%d minutes"), minutes) % (minutes,), seconds)

        elif total_abs < TIME_HOUR * 2 and words:
            return (_("an hour"), 0)

        else:
            hours, seconds = divmod(total_abs, TIME_HOUR)
            if total < 0:
                seconds *= -1

            return (_multi(_("%d hour"), _("%d hours"), hours) % (hours,), seconds)

    elif date_diff.days == 1 and words:
        return (_("tomorrow"), 0)

    elif date_diff.days == -1 and words:
        return (_("yesterday"), 0)

    elif total_abs < TIME_WEEK:
        days, seconds = divmod(total_abs, TIME_DAY)
        if total < 0:
            seconds *= -1
        return (_multi(_("%d day"), _("%d days"), days) % (days,), seconds)

    elif abs(diff.days) == TIME_WEEK and words:
        if total > 0:
            return (_("next week"), diff.seconds)
        else:
            return (_("last week"), diff.seconds)

    # FIXME
    #
    # The biggest reliable unit we can supply to the user is a week (for now?),
    # because we can not safely determine the amount of days in the covered
    # month/year span.

    else:
        weeks, seconds = divmod(total_abs, TIME_WEEK)
        if total < 0:
            seconds *= -1
        return (_multi(_("%d week"), _("%d weeks"), weeks) % (weeks,), seconds)
Exemple #19
0
def compress(t, sign=False, pad=''):
    '''
    Convert the input to compressed format, works with a
    :class:`datetime.timedelta` object or a number that represents the number
    of seconds you want to compress.  If you supply a timestamp or a
    :class:`datetime.datetime` object, it will give the delta relative to the
    current time.

    You can enable showing a sign in front of the compressed format with the
    ``sign`` parameter, the default is not to show signs.

    Optionally, you can chose to pad the output. If you wish your values to be
    separated by spaces, set ``pad`` to ``' '``.

    :param t: seconds or :class:`datetime.timedelta` object
    :param sign: default ``False``
    :param pad: default ``''``

    >>> print(compress(1))
    1s
    >>> print(compress(12))
    12s
    >>> print(compress(123))
    2m3s
    >>> print(compress(1234))
    20m34s
    >>> print(compress(12345))
    3h25m45s
    >>> print(compress(123456))
    1d10h17m36s

    '''

    if isinstance(t, datetime.timedelta):
        seconds = t.seconds + (t.days * 86400)
    elif isinstance(t, six.integer_types + (float, )):
        return compress(datetime.timedelta(seconds=t), sign, pad)
    else:
        return compress(datetime.datetime.now() - _to_datetime(t), sign, pad)

    parts = []
    if sign:
        parts.append('-' if t.days < 0 else '+')

    weeks, seconds = divmod(seconds, TIME_WEEK)
    days, seconds = divmod(seconds, TIME_DAY)
    hours, seconds = divmod(seconds, TIME_HOUR)
    minutes, seconds = divmod(seconds, TIME_MINUTE)

    if weeks:
        parts.append(_('%dw') % (weeks,))
    if days:
        parts.append(_('%dd') % (days,))
    if hours:
        parts.append(_('%dh') % (hours,))
    if minutes:
        parts.append(_('%dm') % (minutes,))
    if seconds:
        parts.append(_('%ds') % (seconds,))

    return pad.join(parts)
Exemple #20
0
def delta(t1, t2, words=True, justnow=datetime.timedelta(seconds=10)):
    '''
    Calculates the estimated delta between two time objects in human-readable
    format. Used internally by the :func:`day` and :func:`duration` functions.

    :param t1: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
               object
    :param t2: timestamp, :class:`datetime.date` or :class:`datetime.datetime`
               object
    :param words: default ``True``, allow words like "yesterday", "tomorrow"
               and "just now"
    :param justnow: default ``datetime.timedelta(seconds=10)``,
               :class:`datetime.timedelta` object representing tolerance for
               considering a delta as meaning 'just now'

    >>> (x,y) = delta(_to_datetime('2012-06-13T15:24:17'), \
    _to_datetime('2013-12-11T12:34:56'))
    >>> print(x)
    77 weeks
    >>> int(y)
    -594639
    '''

    t1 = _to_datetime(t1)
    t2 = _to_datetime(t2)
    diff = t1 - t2
    date_diff = t1.date() - t2.date()

    # The datetime module includes milliseconds with float precision. Floats
    # will give unexpected results here, so we round the value here
    total = math.ceil(_total_seconds(diff))
    total_abs = abs(total)

    if total_abs < TIME_DAY:
        if abs(diff) < justnow and words:
            return (
                _('just now'),
                0,
            )

        elif total_abs < TIME_MINUTE:
            seconds = total_abs
            return (
                _multi(
                    _('%d second'),
                    _('%d seconds'),
                    seconds
                ) % (seconds,),
                0,
            )
        elif total_abs < TIME_MINUTE * 2 and words:
            return (
                _('a minute'),
                0,
            )

        elif total_abs < TIME_HOUR:
            minutes, seconds = divmod(total_abs, TIME_MINUTE)
            if total < 0:
                seconds *= -1
            return (
                _multi(
                    _('%d minute'),
                    _('%d minutes'),
                    minutes
                ) % (minutes,),
                seconds,
            )

        elif total_abs < TIME_HOUR * 2 and words:
            return (
                _('an hour'),
                0,
            )

        else:
            hours, seconds = divmod(total_abs, TIME_HOUR)
            if total < 0:
                seconds *= -1

            return (
                _multi(
                    _('%d hour'),
                    _('%d hours'),
                    hours
                ) % (hours,),
                seconds,
            )

    elif date_diff.days == 1 and words:
        return (_('tomorrow'), 0)

    elif date_diff.days == -1 and words:
        return (_('yesterday'), 0)

    elif total_abs < TIME_WEEK:
        days, seconds = divmod(total_abs, TIME_DAY)
        if total < 0:
            seconds *= -1
        return (
            _multi(
                _('%d day'),
                _('%d days'),
                days
            ) % (days,),
            seconds,
        )

    elif abs(diff.days) == TIME_WEEK and words:
        if total > 0:
            return (_('next week'), diff.seconds)
        else:
            return (_('last week'), diff.seconds)

# FIXME
#
# The biggest reliable unit we can supply to the user is a week (for now?),
# because we can not safely determine the amount of days in the covered
# month/year span.

    else:
        weeks, seconds = divmod(total_abs, TIME_WEEK)
        if total < 0:
            seconds *= -1
        return (
            _multi(
                _('%d week'),
                _('%d weeks'),
                weeks
            ) % (weeks,),
            seconds,
        )