summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Ronacher <armin.ronacher@active-4.com>2013-07-30 02:13:45 +0200
committerArmin Ronacher <armin.ronacher@active-4.com>2013-07-30 02:13:45 +0200
commitb409813eff5142e1856e5e57c221b72d8d5b978b (patch)
tree7069397cc75a81f0d46b0945537f110e4a2df961
parenteead4a3773a241bbf2567f805a7fb26b4cf962a9 (diff)
downloadbabel-b409813eff5142e1856e5e57c221b72d8d5b978b.tar.gz
Added support for territory currency lookups.
The main usecase of this is to figure out at what point in time did a country use a certain currency. The default behavior is to use the current date. This fixes #42
-rw-r--r--CHANGES4
-rw-r--r--babel/numbers.py88
-rw-r--r--docs/api/numbers.rst2
-rwxr-xr-xscripts/import_cldr.py28
-rw-r--r--tests/test_numbers.py23
5 files changed, 144 insertions, 1 deletions
diff --git a/CHANGES b/CHANGES
index 7dbaf25..147124e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,10 @@ Version 2.0
(release date to be decided, codename to be selected)
+- Added support for looking up currencies that belong to a territory
+ through the :func:`babel.numbers.get_territory_currencies`
+ function.
+
Version 1.4
-----------
diff --git a/babel/numbers.py b/babel/numbers.py
index 0f38719..344625e 100644
--- a/babel/numbers.py
+++ b/babel/numbers.py
@@ -21,8 +21,9 @@
from decimal import Decimal, InvalidOperation
import math
import re
+from datetime import date as date_, datetime as datetime_
-from babel.core import default_locale, Locale
+from babel.core import default_locale, Locale, get_global
from babel._compat import range_type
@@ -63,6 +64,91 @@ def get_currency_symbol(currency, locale=LC_NUMERIC):
return Locale.parse(locale).currency_symbols.get(currency, currency)
+def get_territory_currencies(territory, start_date=None, end_date=None,
+ tender=True, non_tender=False,
+ include_details=False):
+ """Returns the list of currencies for the given territory that are valid at
+ the given date range. In addition to that the currency database
+ distinguishes between tender and non-tender currencies. By default only
+ tender currencies are returned.
+
+ The return value is a list of all currencies roughly ordered by the time
+ of when the currency became active. The longer the currency is being in
+ use the more to the left of the list it will be.
+
+ The start date defaults to today. If no end date is given it will be the
+ same as the start date. Otherwise a range can be defined. For instance
+ this can be used to find the currencies in use in Austria between 1995 and
+ 2011:
+
+ >>> from datetime import date
+ >>> get_territory_currencies('AT', date(1995, 1, 1), date(2011, 1, 1))
+ ['ATS', 'EUR']
+
+ Likewise it's also possible to find all the currencies in use on a
+ single date:
+
+ >>> get_territory_currencies('AT', date(1995, 1, 1))
+ ['ATS']
+ >>> get_territory_currencies('AT', date(2011, 1, 1))
+ ['EUR']
+
+ By default the return value only includes tender currencies. This
+ however can be changed:
+
+ >>> get_territory_currencies('US')
+ ['USD']
+ >>> get_territory_currencies('US', tender=False, non_tender=True)
+ ['USN', 'USS']
+
+ .. versionadded:: 2.0
+
+ :param territory: the name of the territory to find the currency fo
+ :param start_date: the start date. If not given today is assumed.
+ :param end_date: the end date. If not given the start date is assumed.
+ :param tender: controls whether tender currencies should be included.
+ :param non_tender: controls whether non-tender currencies should be
+ included.
+ :param include_details: if set to `True`, instead of returning currency
+ codes the return value will be dictionaries
+ with detail information. In that case each
+ dictionary will have the keys ``'currency'``,
+ ``'from'``, ``'to'``, and ``'tender'``.
+ """
+ currencies = get_global('territory_currencies')
+ if start_date is None:
+ start_date = date_.today()
+ elif isinstance(start_date, datetime_):
+ start_date = start_date.date()
+ if end_date is None:
+ end_date = start_date
+ elif isinstance(end_date, datetime_):
+ end_date = end_date.date()
+
+ curs = currencies.get(territory.upper(), ())
+ # TODO: validate that the territory exists
+
+ def _is_active(start, end):
+ return (start is None or start <= end_date) and \
+ (end is None or end >= start_date)
+
+ result = []
+ for currency_code, start, end, is_tender in curs:
+ if ((is_tender and tender) or \
+ (not is_tender and non_tender)) and _is_active(start, end):
+ if include_details:
+ result.append({
+ 'currency': currency_code,
+ 'from': start,
+ 'to': end,
+ 'tender': is_tender,
+ })
+ else:
+ result.append(currency_code)
+
+ return result
+
+
def get_decimal_symbol(locale=LC_NUMERIC):
"""Return the symbol used by the locale to separate decimal fractions.
diff --git a/docs/api/numbers.rst b/docs/api/numbers.rst
index de3573e..207ae0b 100644
--- a/docs/api/numbers.rst
+++ b/docs/api/numbers.rst
@@ -43,3 +43,5 @@ Data Access
.. autofunction:: get_plus_sign_symbol
.. autofunction:: get_minus_sign_symbol
+
+.. autofunction:: get_territory_currencies
diff --git a/scripts/import_cldr.py b/scripts/import_cldr.py
index 84b2b1d..c189e4f 100755
--- a/scripts/import_cldr.py
+++ b/scripts/import_cldr.py
@@ -21,6 +21,8 @@ try:
except ImportError:
from xml.etree import ElementTree
+from datetime import date
+
# Make sure we're using Babel source, and not some previously installed version
sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), '..'))
@@ -95,6 +97,18 @@ def _translate_alias(ctxt, path):
return keys
+def _parse_currency_date(s):
+ if not s:
+ return None
+ parts = s.split('-', 2)
+ return date(*map(int, parts + [1] * (3 - len(parts))))
+
+
+def _currency_sort_key(tup):
+ code, start, end, tender = tup
+ return int(not tender), start or date(1, 1, 1)
+
+
def main():
parser = OptionParser(usage='%prog path/to/cldr')
options, args = parser.parse_args()
@@ -128,6 +142,7 @@ def main():
script_aliases = global_data.setdefault('script_aliases', {})
variant_aliases = global_data.setdefault('variant_aliases', {})
likely_subtags = global_data.setdefault('likely_subtags', {})
+ territory_currencies = global_data.setdefault('territory_currencies', {})
# create auxiliary zone->territory map from the windows zones (we don't set
# the 'zones_territories' map directly here, because there are some zones
@@ -186,6 +201,19 @@ def main():
for likely_subtag in sup_likely.findall('.//likelySubtags/likelySubtag'):
likely_subtags[likely_subtag.attrib['from']] = likely_subtag.attrib['to']
+ # Currencies in territories
+ for region in sup.findall('.//currencyData/region'):
+ region_code = region.attrib['iso3166']
+ region_currencies = []
+ for currency in region.findall('./currency'):
+ cur_start = _parse_currency_date(currency.attrib.get('from'))
+ cur_end = _parse_currency_date(currency.attrib.get('to'))
+ region_currencies.append((currency.attrib['iso4217'],
+ cur_start, cur_end,
+ currency.attrib.get('tender', 'true') == 'true'))
+ region_currencies.sort(key=_currency_sort_key)
+ territory_currencies[region_code] = region_currencies
+
outfile = open(global_path, 'wb')
try:
pickle.dump(global_data, outfile, 2)
diff --git a/tests/test_numbers.py b/tests/test_numbers.py
index 6db4b67..99e0d1b 100644
--- a/tests/test_numbers.py
+++ b/tests/test_numbers.py
@@ -15,6 +15,8 @@ from decimal import Decimal
import unittest
import pytest
+from datetime import date
+
from babel import numbers
@@ -180,6 +182,27 @@ def test_get_currency_symbol():
assert numbers.get_currency_symbol('USD', 'en_US') == u'$'
+def test_get_territory_currencies():
+ assert numbers.get_territory_currencies('AT', date(1995, 1, 1)) == ['ATS']
+ assert numbers.get_territory_currencies('AT', date(2011, 1, 1)) == ['EUR']
+
+ assert numbers.get_territory_currencies('US', date(2013, 1, 1)) == ['USD']
+ assert sorted(numbers.get_territory_currencies('US', date(2013, 1, 1),
+ non_tender=True)) == ['USD', 'USN', 'USS']
+
+ assert numbers.get_territory_currencies('US', date(2013, 1, 1),
+ include_details=True) == [{
+ 'currency': 'USD',
+ 'from': date(1792, 1, 1),
+ 'to': None,
+ 'tender': True
+ }]
+
+ assert numbers.get_territory_currencies('LS', date(2013, 1, 1)) == ['ZAR', 'LSL']
+
+ assert numbers.get_territory_currencies('QO', date(2013, 1, 1)) == []
+
+
def test_get_decimal_symbol():
assert numbers.get_decimal_symbol('en_US') == u'.'