formats.py 7.9 KB

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