locale_.py 4.7 KB
Newer Older
1
# -*- coding: UTF-8 -*-
2 3
# Copyright (C) 2007, 2009 J. David Ibáñez <jdavid.ibp@gmail.com>
# Copyright (C) 2010 Hervé Cauwelier <herve@oursours.net>
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

"""
Output dates and times in locale format.
"""
21 22
from __future__ import absolute_import
import six
23

24 25
# Import from the Standard Library
from decimal import Decimal
26

27
# Import from itools
28
from .accept import get_accept
29 30


31
def get_format(source, accept):
32 33 34 35 36
    # By default use the computer's locale
    if accept is None:
        accept = get_accept()

    # Negotiate
37
    available_languages = six.iterkeys(source)
38 39 40 41 42
    language = accept.select_language(available_languages)
    if language is None:
        language = 'en'

    # The format
43
    return source[language]
44 45


46 47 48 49 50

#
# Date and Time
#

51
def format_date(x, accept=None):
52
    format = get_format(date_formats, accept)[0]
53 54 55 56
    return x.strftime(format)


def format_time(x, accept=None):
57
    format = get_format(date_formats, accept)[1]
58 59 60 61
    return x.strftime(format)


def format_datetime(x, accept=None):
62
    format = get_format(date_formats, accept)[2]
63 64
    return x.strftime(format)

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101


#
# Decimal
#

# http://docs.python.org/library/decimal.html#recipes
# Modified for unicode and trailing currency
def moneyfmt(value, places=2, curr=u'', sep=u',', dp=u'.', pos=u'',
        neg=u'-', trailneg=u''):
    """Convert Decimal to a money formatted unicode.

    places:  required number of places after the decimal point
    curr:    optional currency symbol (may be blank)
    sep:     optional grouping separator (comma, period, space, or blank)
    dp:      decimal point indicator (comma or period)
             only specify as blank when places is zero
    pos:     optional sign for positive numbers: '+', space or blank
    neg:     optional sign for negative numbers: '-', '(', space or blank
    trailneg:optional trailing minus indicator:  '-', ')', space or blank

    >>> d = Decimal('-1234567.8901')
    >>> moneyfmt(d, curr='$')
    '-1,234,567.89$'
    >>> moneyfmt(d, places=0, sep='.', dp='', neg='', trailneg='-')
    '1.234.568-'
    >>> moneyfmt(d, curr='$', neg='(', trailneg=')')
    '(1,234,567.89$)'
    >>> moneyfmt(Decimal(123456789), sep=' ')
    '123 456 789.00'
    >>> moneyfmt(Decimal('-0.02'), neg='<', trailneg='>')
    '<0.02>'

    """
    q = Decimal(10) ** -places      # 2 places --> '0.01'
    sign, digits, exp = value.quantize(q).as_tuple()
    result = []
102 103
    import six
    digits = [six.text_type(d) for d in digits]
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
    build, next = result.append, digits.pop
    if curr:
        build(curr)
    if sign:
        build(trailneg)
    for i in range(places):
        build(next() if digits else u'0')
    build(dp)
    if not digits:
        build(u'0')
    i = 0
    while digits:
        build(next())
        i += 1
        if i == 3 and digits:
            i = 0
            build(sep)
    build(neg if sign else pos)
    return u''.join(reversed(result))


def format_number(x, places=2, curr='', pos=u'', neg=u'-', trailneg=u"",
                  accept=None):
    """Convert Decimal to a number formatted unicode.

    places:  required number of places after the decimal point
    curr:    optional currency symbol (may be blank)
    pos:     optional sign for positive numbers: '+', space or blank
    neg:     optional sign for negative numbers: '-', '(', space or blank
    trailneg:optional trailing minus indicator:  '-', ')', space or blank
    """
    if type(x) is not Decimal:
        x = Decimal(x)
    format = get_format(number_formats, accept)
    return moneyfmt(x, places=places, curr=curr, pos=pos, neg=neg,
            trailneg=trailneg, **format)



###########################################################################
# Initialize the module
###########################################################################

date_formats = {
    # Date, Time, DateTime
    'en': ('%d/%m/%Y', '%H:%M', '%d/%m/%Y %H:%M'),
    'es': ('%d/%m/%Y', '%H.%M', '%d/%m/%Y %H.%M'),
    'fr': ('%d/%m/%Y', '%Hh%M', '%d/%m/%Y %Hh%M'),
    }


number_formats = {
    # See "moneyfmt" docstring for help
    'en': {'sep': u',', 'dp': u'.'},
    'es': {'sep': u'.', 'dp': u','},
    'fr': {'sep': u' ', 'dp': u','},
    }