Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/django/utils/formats.py: 13%
137 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1import datetime
2import decimal
3import functools
4import re
5import unicodedata
6from importlib import import_module
8from django.conf import settings
9from django.utils import dateformat, numberformat
10from django.utils.functional import lazy
11from django.utils.translation import check_for_language, get_language, to_locale
13# format_cache is a mapping from (format_type, lang) to the format string.
14# By using the cache, it is possible to avoid running get_format_modules
15# repeatedly.
16_format_cache = {}
17_format_modules_cache = {}
19ISO_INPUT_FORMATS = {
20 "DATE_INPUT_FORMATS": ["%Y-%m-%d"],
21 "TIME_INPUT_FORMATS": ["%H:%M:%S", "%H:%M:%S.%f", "%H:%M"],
22 "DATETIME_INPUT_FORMATS": [
23 "%Y-%m-%d %H:%M:%S",
24 "%Y-%m-%d %H:%M:%S.%f",
25 "%Y-%m-%d %H:%M",
26 "%Y-%m-%d",
27 ],
28}
31FORMAT_SETTINGS = frozenset(
32 [
33 "DECIMAL_SEPARATOR",
34 "THOUSAND_SEPARATOR",
35 "NUMBER_GROUPING",
36 "FIRST_DAY_OF_WEEK",
37 "MONTH_DAY_FORMAT",
38 "TIME_FORMAT",
39 "DATE_FORMAT",
40 "DATETIME_FORMAT",
41 "SHORT_DATE_FORMAT",
42 "SHORT_DATETIME_FORMAT",
43 "YEAR_MONTH_FORMAT",
44 "DATE_INPUT_FORMATS",
45 "TIME_INPUT_FORMATS",
46 "DATETIME_INPUT_FORMATS",
47 ]
48)
51def reset_format_cache():
52 """Clear any cached formats.
54 This method is provided primarily for testing purposes,
55 so that the effects of cached formats can be removed.
56 """
57 global _format_cache, _format_modules_cache
58 _format_cache = {}
59 _format_modules_cache = {}
62def iter_format_modules(lang, format_module_path=None):
63 """Find format modules."""
64 if not check_for_language(lang):
65 return
67 if format_module_path is None:
68 format_module_path = settings.FORMAT_MODULE_PATH
70 format_locations = []
71 if format_module_path:
72 if isinstance(format_module_path, str):
73 format_module_path = [format_module_path]
74 for path in format_module_path:
75 format_locations.append(path + ".%s")
76 format_locations.append("django.conf.locale.%s")
77 locale = to_locale(lang)
78 locales = [locale]
79 if "_" in locale:
80 locales.append(locale.split("_")[0])
81 for location in format_locations:
82 for loc in locales:
83 try:
84 yield import_module("%s.formats" % (location % loc))
85 except ImportError:
86 pass
89def get_format_modules(lang=None):
90 """Return a list of the format modules found."""
91 if lang is None:
92 lang = get_language()
93 if lang not in _format_modules_cache:
94 _format_modules_cache[lang] = list(
95 iter_format_modules(lang, settings.FORMAT_MODULE_PATH)
96 )
97 return _format_modules_cache[lang]
100def get_format(format_type, lang=None, use_l10n=None):
101 """
102 For a specific format type, return the format for the current
103 language (locale). Default to the format in the settings.
104 format_type is the name of the format, e.g. 'DATE_FORMAT'.
106 If use_l10n is provided and is not None, it forces the value to
107 be localized (or not), overriding the value of settings.USE_L10N.
108 """
109 use_l10n = use_l10n or (
110 use_l10n is None
111 and (
112 settings._USE_L10N_INTERNAL
113 if hasattr(settings, "_USE_L10N_INTERNAL")
114 else settings.USE_L10N
115 )
116 )
117 if use_l10n and lang is None:
118 lang = get_language()
119 cache_key = (format_type, lang)
120 try:
121 return _format_cache[cache_key]
122 except KeyError:
123 pass
125 # The requested format_type has not been cached yet. Try to find it in any
126 # of the format_modules for the given lang if l10n is enabled. If it's not
127 # there or if l10n is disabled, fall back to the project settings.
128 val = None
129 if use_l10n:
130 for module in get_format_modules(lang):
131 val = getattr(module, format_type, None)
132 if val is not None:
133 break
134 if val is None:
135 if format_type not in FORMAT_SETTINGS:
136 return format_type
137 val = getattr(settings, format_type)
138 elif format_type in ISO_INPUT_FORMATS:
139 # If a list of input formats from one of the format_modules was
140 # retrieved, make sure the ISO_INPUT_FORMATS are in this list.
141 val = list(val)
142 for iso_input in ISO_INPUT_FORMATS.get(format_type, ()):
143 if iso_input not in val:
144 val.append(iso_input)
145 _format_cache[cache_key] = val
146 return val
149get_format_lazy = lazy(get_format, str, list, tuple)
152def date_format(value, format=None, use_l10n=None):
153 """
154 Format a datetime.date or datetime.datetime object using a
155 localizable format.
157 If use_l10n is provided and is not None, that will force the value to
158 be localized (or not), overriding the value of settings.USE_L10N.
159 """
160 return dateformat.format(
161 value, get_format(format or "DATE_FORMAT", use_l10n=use_l10n)
162 )
165def time_format(value, format=None, use_l10n=None):
166 """
167 Format a datetime.time object using a localizable format.
169 If use_l10n is provided and is not None, it forces the value to
170 be localized (or not), overriding the value of settings.USE_L10N.
171 """
172 return dateformat.time_format(
173 value, get_format(format or "TIME_FORMAT", use_l10n=use_l10n)
174 )
177def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False):
178 """
179 Format a numeric value using localization settings.
181 If use_l10n is provided and is not None, it forces the value to
182 be localized (or not), overriding the value of settings.USE_L10N.
183 """
184 use_l10n = use_l10n or (
185 use_l10n is None
186 and (
187 settings._USE_L10N_INTERNAL
188 if hasattr(settings, "_USE_L10N_INTERNAL")
189 else settings.USE_L10N
190 )
191 )
192 lang = get_language() if use_l10n else None
193 return numberformat.format(
194 value,
195 get_format("DECIMAL_SEPARATOR", lang, use_l10n=use_l10n),
196 decimal_pos,
197 get_format("NUMBER_GROUPING", lang, use_l10n=use_l10n),
198 get_format("THOUSAND_SEPARATOR", lang, use_l10n=use_l10n),
199 force_grouping=force_grouping,
200 use_l10n=use_l10n,
201 )
204def localize(value, use_l10n=None):
205 """
206 Check if value is a localizable type (date, number...) and return it
207 formatted as a string using current locale format.
209 If use_l10n is provided and is not None, it forces the value to
210 be localized (or not), overriding the value of settings.USE_L10N.
211 """
212 if isinstance(value, str): # Handle strings first for performance reasons.
213 return value
214 elif isinstance(value, bool): # Make sure booleans don't get treated as numbers
215 return str(value)
216 elif isinstance(value, (decimal.Decimal, float, int)):
217 if use_l10n is False:
218 return str(value)
219 return number_format(value, use_l10n=use_l10n)
220 elif isinstance(value, datetime.datetime):
221 return date_format(value, "DATETIME_FORMAT", use_l10n=use_l10n)
222 elif isinstance(value, datetime.date):
223 return date_format(value, use_l10n=use_l10n)
224 elif isinstance(value, datetime.time):
225 return time_format(value, "TIME_FORMAT", use_l10n=use_l10n)
226 return value
229def localize_input(value, default=None):
230 """
231 Check if an input value is a localizable type and return it
232 formatted with the appropriate formatting string of the current locale.
233 """
234 if isinstance(value, str): # Handle strings first for performance reasons.
235 return value
236 elif isinstance(value, bool): # Don't treat booleans as numbers.
237 return str(value)
238 elif isinstance(value, (decimal.Decimal, float, int)):
239 return number_format(value)
240 elif isinstance(value, datetime.datetime):
241 format = default or get_format("DATETIME_INPUT_FORMATS")[0]
242 format = sanitize_strftime_format(format)
243 return value.strftime(format)
244 elif isinstance(value, datetime.date):
245 format = default or get_format("DATE_INPUT_FORMATS")[0]
246 format = sanitize_strftime_format(format)
247 return value.strftime(format)
248 elif isinstance(value, datetime.time):
249 format = default or get_format("TIME_INPUT_FORMATS")[0]
250 return value.strftime(format)
251 return value
254@functools.lru_cache()
255def sanitize_strftime_format(fmt):
256 """
257 Ensure that certain specifiers are correctly padded with leading zeros.
259 For years < 1000 specifiers %C, %F, %G, and %Y don't work as expected for
260 strftime provided by glibc on Linux as they don't pad the year or century
261 with leading zeros. Support for specifying the padding explicitly is
262 available, however, which can be used to fix this issue.
264 FreeBSD, macOS, and Windows do not support explicitly specifying the
265 padding, but return four digit years (with leading zeros) as expected.
267 This function checks whether the %Y produces a correctly padded string and,
268 if not, makes the following substitutions:
270 - %C → %02C
271 - %F → %010F
272 - %G → %04G
273 - %Y → %04Y
275 See https://bugs.python.org/issue13305 for more details.
276 """
277 if datetime.date(1, 1, 1).strftime("%Y") == "0001":
278 return fmt
279 mapping = {"C": 2, "F": 10, "G": 4, "Y": 4}
280 return re.sub(
281 r"((?:^|[^%])(?:%%)*)%([CFGY])",
282 lambda m: r"%s%%0%s%s" % (m[1], mapping[m[2]], m[2]),
283 fmt,
284 )
287def sanitize_separators(value):
288 """
289 Sanitize a value according to the current decimal and
290 thousand separator setting. Used with form field input.
291 """
292 if isinstance(value, str):
293 parts = []
294 decimal_separator = get_format("DECIMAL_SEPARATOR")
295 if decimal_separator in value:
296 value, decimals = value.split(decimal_separator, 1)
297 parts.append(decimals)
298 if settings.USE_THOUSAND_SEPARATOR:
299 thousand_sep = get_format("THOUSAND_SEPARATOR")
300 if (
301 thousand_sep == "."
302 and value.count(".") == 1
303 and len(value.split(".")[-1]) != 3
304 ):
305 # Special case where we suspect a dot meant decimal separator
306 # (see #22171).
307 pass
308 else:
309 for replacement in {
310 thousand_sep,
311 unicodedata.normalize("NFKD", thousand_sep),
312 }:
313 value = value.replace(replacement, "")
314 parts.append(value)
315 value = ".".join(reversed(parts))
316 return value