You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

formats.py 8.5 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import datetime
  2. import decimal
  3. import unicodedata
  4. from importlib import import_module
  5. from django.conf import settings
  6. from django.utils import dateformat, datetime_safe, numberformat, six
  7. from django.utils.encoding import force_str
  8. from django.utils.functional import lazy
  9. from django.utils.safestring import mark_safe
  10. from django.utils.translation import (
  11. check_for_language, get_language, to_locale,
  12. )
  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 = {}
  18. ISO_INPUT_FORMATS = {
  19. 'DATE_INPUT_FORMATS': ['%Y-%m-%d'],
  20. 'TIME_INPUT_FORMATS': ['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'],
  21. 'DATETIME_INPUT_FORMATS': [
  22. '%Y-%m-%d %H:%M:%S',
  23. '%Y-%m-%d %H:%M:%S.%f',
  24. '%Y-%m-%d %H:%M',
  25. '%Y-%m-%d'
  26. ],
  27. }
  28. FORMAT_SETTINGS = frozenset([
  29. 'DECIMAL_SEPARATOR',
  30. 'THOUSAND_SEPARATOR',
  31. 'NUMBER_GROUPING',
  32. 'FIRST_DAY_OF_WEEK',
  33. 'MONTH_DAY_FORMAT',
  34. 'TIME_FORMAT',
  35. 'DATE_FORMAT',
  36. 'DATETIME_FORMAT',
  37. 'SHORT_DATE_FORMAT',
  38. 'SHORT_DATETIME_FORMAT',
  39. 'YEAR_MONTH_FORMAT',
  40. 'DATE_INPUT_FORMATS',
  41. 'TIME_INPUT_FORMATS',
  42. 'DATETIME_INPUT_FORMATS',
  43. ])
  44. def reset_format_cache():
  45. """Clear any cached formats.
  46. This method is provided primarily for testing purposes,
  47. so that the effects of cached formats can be removed.
  48. """
  49. global _format_cache, _format_modules_cache
  50. _format_cache = {}
  51. _format_modules_cache = {}
  52. def iter_format_modules(lang, format_module_path=None):
  53. """
  54. Does the heavy lifting of finding format modules.
  55. """
  56. if not check_for_language(lang):
  57. return
  58. if format_module_path is None:
  59. format_module_path = settings.FORMAT_MODULE_PATH
  60. format_locations = []
  61. if format_module_path:
  62. if isinstance(format_module_path, six.string_types):
  63. format_module_path = [format_module_path]
  64. for path in format_module_path:
  65. format_locations.append(path + '.%s')
  66. format_locations.append('django.conf.locale.%s')
  67. locale = to_locale(lang)
  68. locales = [locale]
  69. if '_' in locale:
  70. locales.append(locale.split('_')[0])
  71. for location in format_locations:
  72. for loc in locales:
  73. try:
  74. yield import_module('%s.formats' % (location % loc))
  75. except ImportError:
  76. pass
  77. def get_format_modules(lang=None, reverse=False):
  78. """
  79. Returns a list of the format modules found
  80. """
  81. if lang is None:
  82. lang = get_language()
  83. modules = _format_modules_cache.setdefault(lang, list(iter_format_modules(lang, settings.FORMAT_MODULE_PATH)))
  84. if reverse:
  85. return list(reversed(modules))
  86. return modules
  87. def get_format(format_type, lang=None, use_l10n=None):
  88. """
  89. For a specific format type, returns the format for the current
  90. language (locale), defaults to the format in the settings.
  91. format_type is the name of the format, e.g. 'DATE_FORMAT'
  92. If use_l10n is provided and is not None, that will force the value to
  93. be localized (or not), overriding the value of settings.USE_L10N.
  94. """
  95. format_type = force_str(format_type)
  96. if use_l10n or (use_l10n is None and settings.USE_L10N):
  97. if lang is None:
  98. lang = get_language()
  99. cache_key = (format_type, lang)
  100. try:
  101. cached = _format_cache[cache_key]
  102. if cached is not None:
  103. return cached
  104. except KeyError:
  105. for module in get_format_modules(lang):
  106. try:
  107. val = getattr(module, format_type)
  108. for iso_input in ISO_INPUT_FORMATS.get(format_type, ()):
  109. if iso_input not in val:
  110. if isinstance(val, tuple):
  111. val = list(val)
  112. val.append(iso_input)
  113. _format_cache[cache_key] = val
  114. return val
  115. except AttributeError:
  116. pass
  117. _format_cache[cache_key] = None
  118. if format_type not in FORMAT_SETTINGS:
  119. return format_type
  120. # Return the general setting by default
  121. return getattr(settings, format_type)
  122. get_format_lazy = lazy(get_format, six.text_type, list, tuple)
  123. def date_format(value, format=None, use_l10n=None):
  124. """
  125. Formats a datetime.date or datetime.datetime object using a
  126. localizable format
  127. If use_l10n is provided and is not None, that will force the value to
  128. be localized (or not), overriding the value of settings.USE_L10N.
  129. """
  130. return dateformat.format(value, get_format(format or 'DATE_FORMAT', use_l10n=use_l10n))
  131. def time_format(value, format=None, use_l10n=None):
  132. """
  133. Formats a datetime.time object using a localizable format
  134. If use_l10n is provided and is not None, that will force the value to
  135. be localized (or not), overriding the value of settings.USE_L10N.
  136. """
  137. return dateformat.time_format(value, get_format(format or 'TIME_FORMAT', use_l10n=use_l10n))
  138. def number_format(value, decimal_pos=None, use_l10n=None, force_grouping=False):
  139. """
  140. Formats a numeric value using localization settings
  141. If use_l10n is provided and is not None, that will force the value to
  142. be localized (or not), overriding the value of settings.USE_L10N.
  143. """
  144. if use_l10n or (use_l10n is None and settings.USE_L10N):
  145. lang = get_language()
  146. else:
  147. lang = None
  148. return numberformat.format(
  149. value,
  150. get_format('DECIMAL_SEPARATOR', lang, use_l10n=use_l10n),
  151. decimal_pos,
  152. get_format('NUMBER_GROUPING', lang, use_l10n=use_l10n),
  153. get_format('THOUSAND_SEPARATOR', lang, use_l10n=use_l10n),
  154. force_grouping=force_grouping
  155. )
  156. def localize(value, use_l10n=None):
  157. """
  158. Checks if value is a localizable type (date, number...) and returns it
  159. formatted as a string using current locale format.
  160. If use_l10n is provided and is not None, that will force the value to
  161. be localized (or not), overriding the value of settings.USE_L10N.
  162. """
  163. if isinstance(value, bool):
  164. return mark_safe(six.text_type(value))
  165. elif isinstance(value, (decimal.Decimal, float) + six.integer_types):
  166. return number_format(value, use_l10n=use_l10n)
  167. elif isinstance(value, datetime.datetime):
  168. return date_format(value, 'DATETIME_FORMAT', use_l10n=use_l10n)
  169. elif isinstance(value, datetime.date):
  170. return date_format(value, use_l10n=use_l10n)
  171. elif isinstance(value, datetime.time):
  172. return time_format(value, 'TIME_FORMAT', use_l10n=use_l10n)
  173. else:
  174. return value
  175. def localize_input(value, default=None):
  176. """
  177. Checks if an input value is a localizable type and returns it
  178. formatted with the appropriate formatting string of the current locale.
  179. """
  180. if isinstance(value, (decimal.Decimal, float) + six.integer_types):
  181. return number_format(value)
  182. elif isinstance(value, datetime.datetime):
  183. value = datetime_safe.new_datetime(value)
  184. format = force_str(default or get_format('DATETIME_INPUT_FORMATS')[0])
  185. return value.strftime(format)
  186. elif isinstance(value, datetime.date):
  187. value = datetime_safe.new_date(value)
  188. format = force_str(default or get_format('DATE_INPUT_FORMATS')[0])
  189. return value.strftime(format)
  190. elif isinstance(value, datetime.time):
  191. format = force_str(default or get_format('TIME_INPUT_FORMATS')[0])
  192. return value.strftime(format)
  193. return value
  194. def sanitize_separators(value):
  195. """
  196. Sanitizes a value according to the current decimal and
  197. thousand separator setting. Used with form field input.
  198. """
  199. if settings.USE_L10N and isinstance(value, six.string_types):
  200. parts = []
  201. decimal_separator = get_format('DECIMAL_SEPARATOR')
  202. if decimal_separator in value:
  203. value, decimals = value.split(decimal_separator, 1)
  204. parts.append(decimals)
  205. if settings.USE_THOUSAND_SEPARATOR:
  206. thousand_sep = get_format('THOUSAND_SEPARATOR')
  207. if thousand_sep == '.' and value.count('.') == 1 and len(value.split('.')[-1]) != 3:
  208. # Special case where we suspect a dot meant decimal separator (see #22171)
  209. pass
  210. else:
  211. for replacement in {
  212. thousand_sep, unicodedata.normalize('NFKD', thousand_sep)}:
  213. value = value.replace(replacement, '')
  214. parts.append(value)
  215. value = '.'.join(reversed(parts))
  216. return value