convert an integer to its ordinal as a string 1 is 1st 2 is 2nd 3 is 3rd etc works for any integer translators ordinal format for 11 11th 12 12th and 13 13th translators ordinal format when value ends with 0 e g 80th translators ordinal format when value ends with 1 e g 81st except 11 translators ordinal format when value ends with 2 e g 82nd except 12 translators ordinal format when value ends with 3 e g 83rd except 13 translators ordinal format when value ends with 4 e g 84th translators ordinal format when value ends with 5 e g 85th translators ordinal format when value ends with 6 e g 86th translators ordinal format when value ends with 7 e g 87th translators ordinal format when value ends with 8 e g 88th translators ordinal format when value ends with 9 e g 89th mark value safe so i18n does not break with sup or sub see 19988 convert an integer to a string containing commas every three digits for example 3000 becomes 3 000 and 45000 becomes 45 000 a tuple of standard large number to their converters convert a large integer to a friendly text representation works best for numbers over 1 million for example 1000000 becomes 1 0 million 1200000 becomes 1 2 million and 1200000000 becomes 1 2 billion for numbers 19 return the number spelled out otherwise return the number this follows associated press style perform the comparison in the default time zone when usetz true unless a specific time zone has been applied with the timezone filter for date values that are tomorrow today or yesterday compared to present day return representing string otherwise return a string formatted according to settings dateformat passed value wasn t a date object this filter doesn t require expectslocaltimetrue because it deals properly with both naive and aware datetimes therefore avoid the cost of conversion for date and time values show how many seconds minutes or hours ago compared to current timestamp return representing string translators delta will contain a string like 2 months or 1 month 2 weeks translators please keep a nonbreaking space u00a0 between count and time unit translators please keep a nonbreaking space u00a0 between count and time unit translators please keep a nonbreaking space u00a0 between count and time unit translators please keep a nonbreaking space u00a0 between count and time unit translators please keep a nonbreaking space u00a0 between count and time unit translators please keep a nonbreaking space u00a0 between count and time unit translators delta will contain a string like 2 months or 1 month 2 weeks translators naturaltimepast strings will be included in deltas ago translators naturaltimefuture strings will be included in deltas from now convert an integer to its ordinal as a string 1 is 1st 2 is 2nd 3 is 3rd etc works for any integer translators ordinal format for 11 11th 12 12th and 13 13th translators ordinal format when value ends with 0 e g 80th translators ordinal format when value ends with 1 e g 81st except 11 translators ordinal format when value ends with 2 e g 82nd except 12 translators ordinal format when value ends with 3 e g 83rd except 13 translators ordinal format when value ends with 4 e g 84th translators ordinal format when value ends with 5 e g 85th translators ordinal format when value ends with 6 e g 86th translators ordinal format when value ends with 7 e g 87th translators ordinal format when value ends with 8 e g 88th translators ordinal format when value ends with 9 e g 89th mark value safe so i18n does not break with sup or sub see 19988 convert an integer to a string containing commas every three digits for example 3000 becomes 3 000 and 45000 becomes 45 000 a tuple of standard large number to their converters convert a large integer to a friendly text representation works best for numbers over 1 million for example 1000000 becomes 1 0 million 1200000 becomes 1 2 million and 1200000000 becomes 1 2 billion for numbers 1 9 return the number spelled out otherwise return the number this follows associated press style perform the comparison in the default time zone when use_tz true unless a specific time zone has been applied with the timezone filter for date values that are tomorrow today or yesterday compared to present day return representing string otherwise return a string formatted according to settings date_format passed value wasn t a date object this filter doesn t require expects_localtime true because it deals properly with both naive and aware datetimes therefore avoid the cost of conversion for date and time values show how many seconds minutes or hours ago compared to current timestamp return representing string translators delta will contain a string like 2 months or 1 month 2 weeks translators please keep a non breaking space u 00a0 between count and time unit translators please keep a non breaking space u 00a0 between count and time unit translators please keep a non breaking space u 00a0 between count and time unit translators please keep a non breaking space u 00a0 between count and time unit translators please keep a non breaking space u 00a0 between count and time unit translators please keep a non breaking space u 00a0 between count and time unit translators delta will contain a string like 2 months or 1 month 2 weeks translators naturaltime past strings will be included in delta s ago translators naturaltime future strings will be included in delta s from now datetime is a subclass of date
import re
from datetime import date, datetime, timezone
from decimal import Decimal

from django import template
from django.template import defaultfilters
from django.utils.formats import number_format
from django.utils.safestring import mark_safe
from django.utils.timezone import is_aware
from django.utils.translation import gettext as _
from django.utils.translation import (
    gettext_lazy,
    ngettext,
    ngettext_lazy,
    npgettext_lazy,
    pgettext,
    round_away_from_one,
)

register = template.Library()


@register.filter(is_safe=True)
def ordinal(value):
    
    try:
        value = int(value)
    except (TypeError, ValueError):
        return value
    if value % 100 in (11, 12, 13):
        
        value = pgettext("ordinal 11, 12, 13", "{}th").format(value)
    else:
        templates = (
            
            pgettext("ordinal 0", "{}th"),
            
            pgettext("ordinal 1", "{}st"),
            
            pgettext("ordinal 2", "{}nd"),
            
            pgettext("ordinal 3", "{}rd"),
            
            pgettext("ordinal 4", "{}th"),
            
            pgettext("ordinal 5", "{}th"),
            
            pgettext("ordinal 6", "{}th"),
            
            pgettext("ordinal 7", "{}th"),
            
            pgettext("ordinal 8", "{}th"),
            
            pgettext("ordinal 9", "{}th"),
        )
        value = templates[value % 10].format(value)
    
    return mark_safe(value)


@register.filter(is_safe=True)
def intcomma(value, use_l10n=True):
    
    if use_l10n:
        try:
            if not isinstance(value, (float, Decimal)):
                value = int(value)
        except (TypeError, ValueError):
            return intcomma(value, False)
        else:
            return number_format(value, use_l10n=True, force_grouping=True)
    orig = str(value)
    new = re.sub(r"^(-?\d+)(\d{3})", r"\g<1>,\g<2>", orig)
    if orig == new:
        return new
    else:
        return intcomma(new, use_l10n)



intword_converters = (
    (6, lambda number: ngettext("%(value)s million", "%(value)s million", number)),
    (9, lambda number: ngettext("%(value)s billion", "%(value)s billion", number)),
    (12, lambda number: ngettext("%(value)s trillion", "%(value)s trillion", number)),
    (
        15,
        lambda number: ngettext(
            "%(value)s quadrillion", "%(value)s quadrillion", number
        ),
    ),
    (
        18,
        lambda number: ngettext(
            "%(value)s quintillion", "%(value)s quintillion", number
        ),
    ),
    (
        21,
        lambda number: ngettext("%(value)s sextillion", "%(value)s sextillion", number),
    ),
    (
        24,
        lambda number: ngettext("%(value)s septillion", "%(value)s septillion", number),
    ),
    (27, lambda number: ngettext("%(value)s octillion", "%(value)s octillion", number)),
    (30, lambda number: ngettext("%(value)s nonillion", "%(value)s nonillion", number)),
    (33, lambda number: ngettext("%(value)s decillion", "%(value)s decillion", number)),
    (100, lambda number: ngettext("%(value)s googol", "%(value)s googol", number)),
)


@register.filter(is_safe=False)
def intword(value):
    
    try:
        value = int(value)
    except (TypeError, ValueError):
        return value

    abs_value = abs(value)
    if abs_value < 1000000:
        return value

    for exponent, converter in intword_converters:
        large_number = 10**exponent
        if abs_value < large_number * 1000:
            new_value = value / large_number
            rounded_value = round_away_from_one(new_value)
            return converter(abs(rounded_value)) % {
                "value": defaultfilters.floatformat(new_value, 1),
            }
    return value


@register.filter(is_safe=True)
def apnumber(value):
    
    try:
        value = int(value)
    except (TypeError, ValueError):
        return value
    if not 0 < value < 10:
        return value
    return (
        _("one"),
        _("two"),
        _("three"),
        _("four"),
        _("five"),
        _("six"),
        _("seven"),
        _("eight"),
        _("nine"),
    )[value - 1]




@register.filter(expects_localtime=True)
def naturalday(value, arg=None):
    
    tzinfo = getattr(value, "tzinfo", None)
    try:
        value = date(value.year, value.month, value.day)
    except AttributeError:
        
        return value
    today = datetime.now(tzinfo).date()
    delta = value - today
    if delta.days == 0:
        return _("today")
    elif delta.days == 1:
        return _("tomorrow")
    elif delta.days == -1:
        return _("yesterday")
    return defaultfilters.date(value, arg)




@register.filter
def naturaltime(value):
    
    return NaturalTimeFormatter.string_for(value)


class NaturalTimeFormatter:
    time_strings = {
        
        "past-day": gettext_lazy("%(delta)s ago"),
        
        
        "past-hour": ngettext_lazy("an hour ago", "%(count)s hours ago", "count"),
        
        
        "past-minute": ngettext_lazy("a minute ago", "%(count)s minutes ago", "count"),
        
        
        "past-second": ngettext_lazy("a second ago", "%(count)s seconds ago", "count"),
        "now": gettext_lazy("now"),
        
        
        "future-second": ngettext_lazy(
            "a second from now", "%(count)s seconds from now", "count"
        ),
        
        
        "future-minute": ngettext_lazy(
            "a minute from now", "%(count)s minutes from now", "count"
        ),
        
        
        "future-hour": ngettext_lazy(
            "an hour from now", "%(count)s hours from now", "count"
        ),
        
        "future-day": gettext_lazy("%(delta)s from now"),
    }
    past_substrings = {
        
        "year": npgettext_lazy(
            "naturaltime-past", "%(num)d year", "%(num)d years", "num"
        ),
        "month": npgettext_lazy(
            "naturaltime-past", "%(num)d month", "%(num)d months", "num"
        ),
        "week": npgettext_lazy(
            "naturaltime-past", "%(num)d week", "%(num)d weeks", "num"
        ),
        "day": npgettext_lazy("naturaltime-past", "%(num)d day", "%(num)d days", "num"),
        "hour": npgettext_lazy(
            "naturaltime-past", "%(num)d hour", "%(num)d hours", "num"
        ),
        "minute": npgettext_lazy(
            "naturaltime-past", "%(num)d minute", "%(num)d minutes", "num"
        ),
    }
    future_substrings = {
        
        
        "year": npgettext_lazy(
            "naturaltime-future", "%(num)d year", "%(num)d years", "num"
        ),
        "month": npgettext_lazy(
            "naturaltime-future", "%(num)d month", "%(num)d months", "num"
        ),
        "week": npgettext_lazy(
            "naturaltime-future", "%(num)d week", "%(num)d weeks", "num"
        ),
        "day": npgettext_lazy(
            "naturaltime-future", "%(num)d day", "%(num)d days", "num"
        ),
        "hour": npgettext_lazy(
            "naturaltime-future", "%(num)d hour", "%(num)d hours", "num"
        ),
        "minute": npgettext_lazy(
            "naturaltime-future", "%(num)d minute", "%(num)d minutes", "num"
        ),
    }

    @classmethod
    def string_for(cls, value):
        if not isinstance(value, date):  
            return value

        now = datetime.now(timezone.utc if is_aware(value) else None)
        if value < now:
            delta = now - value
            if delta.days != 0:
                return cls.time_strings["past-day"] % {
                    "delta": defaultfilters.timesince(
                        value, now, time_strings=cls.past_substrings
                    ),
                }
            elif delta.seconds == 0:
                return cls.time_strings["now"]
            elif delta.seconds < 60:
                return cls.time_strings["past-second"] % {"count": delta.seconds}
            elif delta.seconds // 60 < 60:
                count = delta.seconds // 60
                return cls.time_strings["past-minute"] % {"count": count}
            else:
                count = delta.seconds // 60 // 60
                return cls.time_strings["past-hour"] % {"count": count}
        else:
            delta = value - now
            if delta.days != 0:
                return cls.time_strings["future-day"] % {
                    "delta": defaultfilters.timeuntil(
                        value, now, time_strings=cls.future_substrings
                    ),
                }
            elif delta.seconds == 0:
                return cls.time_strings["now"]
            elif delta.seconds < 60:
                return cls.time_strings["future-second"] % {"count": delta.seconds}
            elif delta.seconds // 60 < 60:
                count = delta.seconds // 60
                return cls.time_strings["future-minute"] % {"count": count}
            else:
                count = delta.seconds // 60 // 60
                return cls.time_strings["future-hour"] % {"count": count}