formatcache is a mapping from formattype lang to the format string by using the cache it is possible to avoid running getformatmodules repeatedly clear any cached formats this method is provided primarily for testing purposes so that the effects of cached formats can be removed find format modules if not checkforlanguagelang return if formatmodulepath is none formatmodulepath settings formatmodulepath formatlocations if formatmodulepath if isinstanceformatmodulepath str formatmodulepath formatmodulepath for path in formatmodulepath formatlocations appendpath s formatlocations appenddjango conf locale s locale tolocalelang locales locale if in locale locales appendlocale split0 for location in formatlocations for loc in locales try yield importmodules formats location loc except importerror pass def getformatmoduleslangnone for a specific format type return the format for the current language locale default to the format in the settings formattype is the name of the format e g dateformat if usel10n is provided and is not none it forces the value to be localized or not otherwise it s always localized the requested formattype has not been cached yet try to find it in any of the formatmodules for the given lang if l10n is enabled if it s not there or if l10n is disabled fall back to the project settings if a list of input formats from one of the formatmodules was retrieved make sure the isoinputformats are in this list format a datetime date or datetime datetime object using a localizable format if usel10n is provided and is not none that will force the value to be localized or not otherwise it s always localized format a datetime time object using a localizable format if usel10n is provided and is not none it forces the value to be localized or not otherwise it s always localized format a numeric value using localization settings if usel10n is provided and is not none it forces the value to be localized or not otherwise it s always localized check if value is a localizable type date number and return it formatted as a string using current locale format if usel10n is provided and is not none it forces the value to be localized or not otherwise it s always localized check if an input value is a localizable type and return it formatted with the appropriate formatting string of the current locale ensure that certain specifiers are correctly padded with leading zeros for years 1000 specifiers c f g and y don t work as expected for strftime provided by glibc on linux as they don t pad the year or century with leading zeros support for specifying the padding explicitly is available however which can be used to fix this issue freebsd macos and windows do not support explicitly specifying the padding but return four digit years with leading zeros as expected this function checks whether the y produces a correctly padded string and if not makes the following substitutions c 02c f 010f g 04g y 04y see https bugs python orgissue13305 for more details sanitize a value according to the current decimal and thousand separator setting used with form field input special case where we suspect a dot meant decimal separator see 22171 format_cache is a mapping from format_type lang to the format string by using the cache it is possible to avoid running get_format_modules repeatedly clear any cached formats this method is provided primarily for testing purposes so that the effects of cached formats can be removed find format modules return a list of the format modules found for a specific format type return the format for the current language locale default to the format in the settings format_type is the name of the format e g date_format if use_l10n is provided and is not none it forces the value to be localized or not otherwise it s always localized format_type may be lazy the requested format_type has not been cached yet try to find it in any of the format_modules for the given lang if l10n is enabled if it s not there or if l10n is disabled fall back to the project settings if a list of input formats from one of the format_modules was retrieved make sure the iso_input_formats are in this list format a datetime date or datetime datetime object using a localizable format if use_l10n is provided and is not none that will force the value to be localized or not otherwise it s always localized format a datetime time object using a localizable format if use_l10n is provided and is not none it forces the value to be localized or not otherwise it s always localized format a numeric value using localization settings if use_l10n is provided and is not none it forces the value to be localized or not otherwise it s always localized check if value is a localizable type date number and return it formatted as a string using current locale format if use_l10n is provided and is not none it forces the value to be localized or not otherwise it s always localized handle strings first for performance reasons make sure booleans don t get treated as numbers check if an input value is a localizable type and return it formatted with the appropriate formatting string of the current locale handle strings first for performance reasons don t treat booleans as numbers ensure that certain specifiers are correctly padded with leading zeros for years 1000 specifiers c f g and y don t work as expected for strftime provided by glibc on linux as they don t pad the year or century with leading zeros support for specifying the padding explicitly is available however which can be used to fix this issue freebsd macos and windows do not support explicitly specifying the padding but return four digit years with leading zeros as expected this function checks whether the y produces a correctly padded string and if not makes the following substitutions c 02c f 010f g 04g y 04y see https bugs python org issue13305 for more details sanitize a value according to the current decimal and thousand separator setting used with form field input special case where we suspect a dot meant decimal separator see 22171
import datetime
import decimal
import functools
import re
import unicodedata
from importlib import import_module

from django.conf import settings
from django.utils import dateformat, numberformat
from django.utils.functional import lazy
from django.utils.translation import check_for_language, get_language, to_locale




_format_cache = {}
_format_modules_cache = {}

ISO_INPUT_FORMATS = {
    "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
    "TIME_INPUT_FORMATS": ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"],
    "DATETIME_INPUT_FORMATS": [
        "%Y-%m-%d %H:%M:%S",
        "%Y-%m-%d %H:%M:%S.%f",
        "%Y-%m-%d %H:%M",
        "%Y-%m-%d",
    ],
}


FORMAT_SETTINGS = frozenset(
    [
        "DECIMAL_SEPARATOR",
        "THOUSAND_SEPARATOR",
        "NUMBER_GROUPING",
        "FIRST_DAY_OF_WEEK",
        "MONTH_DAY_FORMAT",
        "TIME_FORMAT",
        "DATE_FORMAT",
        "DATETIME_FORMAT",
        "SHORT_DATE_FORMAT",
        "SHORT_DATETIME_FORMAT",
        "YEAR_MONTH_FORMAT",
        "DATE_INPUT_FORMATS",
        "TIME_INPUT_FORMATS",
        "DATETIME_INPUT_FORMATS",
    ]
)


def reset_format_cache():
    
    global _format_cache, _format_modules_cache
    _format_cache = {}
    _format_modules_cache = {}


def iter_format_modules(lang, format_module_path=None):
    
    if not check_for_language(lang):
        return

    if format_module_path is None:
        format_module_path = settings.FORMAT_MODULE_PATH

    format_locations = []
    if format_module_path:
        if isinstance(format_module_path, str):
            format_module_path = [format_module_path]
        for path in format_module_path:
            format_locations.append(path + ".%s")
    format_locations.append("django.conf.locale.%s")
    locale = to_locale(lang)
    locales = [locale]
    if "_" in locale:
        locales.append(locale.split("_")[0])
    for location in format_locations:
        for loc in locales:
            try:
                yield import_module("%s.formats" % (location % loc))
            except ImportError:
                pass


def get_format_modules(lang=None):
    
    if lang is None:
        lang = get_language()
    if lang not in _format_modules_cache:
        _format_modules_cache[lang] = list(
            iter_format_modules(lang, settings.FORMAT_MODULE_PATH)
        )
    return _format_modules_cache[lang]


def get_format(format_type, lang=None, use_l10n=None):
    
    if use_l10n is None:
        use_l10n = True
    if use_l10n and lang is None:
        lang = get_language()
    format_type = str(format_type)  
    cache_key = (format_type, lang)
    try:
        return _format_cache[cache_key]
    except KeyError:
        pass

    
    
    
    val = None
    if use_l10n:
        for module in get_format_modules(lang):
            val = getattr(module, format_type, None)
            if val is not None:
                break
    if val is None:
        if format_type not in FORMAT_SETTINGS:
            return format_type
        val = getattr(settings, format_type)
    elif format_type in ISO_INPUT_FORMATS:
        
        
        val = list(val)
        for iso_input in ISO_INPUT_FORMATS.get(format_type, ()):
            if iso_input not in val:
                val.append(iso_input)
    _format_cache[cache_key] = val
    return val


get_format_lazy = lazy(get_format, str, list, tuple)


def date_format(value, format=None, use_l10n=None):
    
    return dateformat.format(
        value, get_format(format or "DATE_FORMAT", use_l10n=use_l10n)
    )


def time_format(value, format=None, use_l10n=None):
    
    return dateformat.time_format(
        value, get_format(format or "TIME_FORMAT", use_l10n=use_l10n)
    )


def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False):
    
    if use_l10n is None:
        use_l10n = True
    lang = get_language() if use_l10n else None
    return numberformat.format(
        value,
        get_format("DECIMAL_SEPARATOR", lang, use_l10n=use_l10n),
        decimal_pos,
        get_format("NUMBER_GROUPING", lang, use_l10n=use_l10n),
        get_format("THOUSAND_SEPARATOR", lang, use_l10n=use_l10n),
        force_grouping=force_grouping,
        use_l10n=use_l10n,
    )


def localize(value, use_l10n=None):
    
    if isinstance(value, str):  
        return value
    elif isinstance(value, bool):  
        return str(value)
    elif isinstance(value, (decimal.Decimal, float, int)):
        if use_l10n is False:
            return str(value)
        return number_format(value, use_l10n=use_l10n)
    elif isinstance(value, datetime.datetime):
        return date_format(value, "DATETIME_FORMAT", use_l10n=use_l10n)
    elif isinstance(value, datetime.date):
        return date_format(value, use_l10n=use_l10n)
    elif isinstance(value, datetime.time):
        return time_format(value, use_l10n=use_l10n)
    return value


def localize_input(value, default=None):
    
    if isinstance(value, str):  
        return value
    elif isinstance(value, bool):  
        return str(value)
    elif isinstance(value, (decimal.Decimal, float, int)):
        return number_format(value)
    elif isinstance(value, datetime.datetime):
        format = default or get_format("DATETIME_INPUT_FORMATS")[0]
        format = sanitize_strftime_format(format)
        return value.strftime(format)
    elif isinstance(value, datetime.date):
        format = default or get_format("DATE_INPUT_FORMATS")[0]
        format = sanitize_strftime_format(format)
        return value.strftime(format)
    elif isinstance(value, datetime.time):
        format = default or get_format("TIME_INPUT_FORMATS")[0]
        return value.strftime(format)
    return value


@functools.lru_cache
def sanitize_strftime_format(fmt):
    
    if datetime.date(1, 1, 1).strftime("%Y") == "0001":
        return fmt
    mapping = {"C": 2, "F": 10, "G": 4, "Y": 4}
    return re.sub(
        r"((?:^|[^%])(?:%%)*)%([CFGY])",
        lambda m: r"%s%%0%s%s" % (m[1], mapping[m[2]], m[2]),
        fmt,
    )


def sanitize_separators(value):
    
    if isinstance(value, str):
        parts = []
        decimal_separator = get_format("DECIMAL_SEPARATOR")
        if decimal_separator in value:
            value, decimals = value.split(decimal_separator, 1)
            parts.append(decimals)
        if settings.USE_THOUSAND_SEPARATOR:
            thousand_sep = get_format("THOUSAND_SEPARATOR")
            if (
                thousand_sep == "."
                and value.count(".") == 1
                and len(value.split(".")[-1]) != 3
            ):
                
                
                pass
            else:
                for replacement in {
                    thousand_sep,
                    unicodedata.normalize("NFKD", thousand_sep),
                }:
                    value = value.replace(replacement, "")
        parts.append(value)
        value = ".".join(reversed(parts))
    return value