186 lines
4.8 KiB
Go
186 lines
4.8 KiB
Go
|
// Copyright 2015 The Go Authors. All rights reserved.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
|
||
|
//go:generate go run gen.go gen_common.go -output tables.go
|
||
|
|
||
|
// Package currency contains currency-related functionality.
|
||
|
//
|
||
|
// NOTE: the formatting functionality is currently under development and may
|
||
|
// change without notice.
|
||
|
package currency // import "golang.org/x/text/currency"
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"sort"
|
||
|
|
||
|
"golang.org/x/text/internal/tag"
|
||
|
"golang.org/x/text/language"
|
||
|
)
|
||
|
|
||
|
// TODO:
|
||
|
// - language-specific currency names.
|
||
|
// - currency formatting.
|
||
|
// - currency information per region
|
||
|
// - register currency code (there are no private use area)
|
||
|
|
||
|
// TODO: remove Currency type from package language.
|
||
|
|
||
|
// Kind determines the rounding and rendering properties of a currency value.
|
||
|
type Kind struct {
|
||
|
rounding rounding
|
||
|
// TODO: formatting type: standard, accounting. See CLDR.
|
||
|
}
|
||
|
|
||
|
type rounding byte
|
||
|
|
||
|
const (
|
||
|
standard rounding = iota
|
||
|
cash
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// Standard defines standard rounding and formatting for currencies.
|
||
|
Standard Kind = Kind{rounding: standard}
|
||
|
|
||
|
// Cash defines rounding and formatting standards for cash transactions.
|
||
|
Cash Kind = Kind{rounding: cash}
|
||
|
|
||
|
// Accounting defines rounding and formatting standards for accounting.
|
||
|
Accounting Kind = Kind{rounding: standard}
|
||
|
)
|
||
|
|
||
|
// Rounding reports the rounding characteristics for the given currency, where
|
||
|
// scale is the number of fractional decimals and increment is the number of
|
||
|
// units in terms of 10^(-scale) to which to round to.
|
||
|
func (k Kind) Rounding(cur Unit) (scale, increment int) {
|
||
|
info := currency.Elem(int(cur.index))[3]
|
||
|
switch k.rounding {
|
||
|
case standard:
|
||
|
info &= roundMask
|
||
|
case cash:
|
||
|
info >>= cashShift
|
||
|
}
|
||
|
return int(roundings[info].scale), int(roundings[info].increment)
|
||
|
}
|
||
|
|
||
|
// Unit is an ISO 4217 currency designator.
|
||
|
type Unit struct {
|
||
|
index uint16
|
||
|
}
|
||
|
|
||
|
// String returns the ISO code of u.
|
||
|
func (u Unit) String() string {
|
||
|
if u.index == 0 {
|
||
|
return "XXX"
|
||
|
}
|
||
|
return currency.Elem(int(u.index))[:3]
|
||
|
}
|
||
|
|
||
|
// Amount creates an Amount for the given currency unit and amount.
|
||
|
func (u Unit) Amount(amount interface{}) Amount {
|
||
|
// TODO: verify amount is a supported number type
|
||
|
return Amount{amount: amount, currency: u}
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
errSyntax = errors.New("currency: tag is not well-formed")
|
||
|
errValue = errors.New("currency: tag is not a recognized currency")
|
||
|
)
|
||
|
|
||
|
// ParseISO parses a 3-letter ISO 4217 currency code. It returns an error if s
|
||
|
// is not well-formed or not a recognized currency code.
|
||
|
func ParseISO(s string) (Unit, error) {
|
||
|
var buf [4]byte // Take one byte more to detect oversize keys.
|
||
|
key := buf[:copy(buf[:], s)]
|
||
|
if !tag.FixCase("XXX", key) {
|
||
|
return Unit{}, errSyntax
|
||
|
}
|
||
|
if i := currency.Index(key); i >= 0 {
|
||
|
if i == xxx {
|
||
|
return Unit{}, nil
|
||
|
}
|
||
|
return Unit{uint16(i)}, nil
|
||
|
}
|
||
|
return Unit{}, errValue
|
||
|
}
|
||
|
|
||
|
// MustParseISO is like ParseISO, but panics if the given currency unit
|
||
|
// cannot be parsed. It simplifies safe initialization of Unit values.
|
||
|
func MustParseISO(s string) Unit {
|
||
|
c, err := ParseISO(s)
|
||
|
if err != nil {
|
||
|
panic(err)
|
||
|
}
|
||
|
return c
|
||
|
}
|
||
|
|
||
|
// FromRegion reports the currency unit that is currently legal tender in the
|
||
|
// given region according to CLDR. It will return false if region currently does
|
||
|
// not have a legal tender.
|
||
|
func FromRegion(r language.Region) (currency Unit, ok bool) {
|
||
|
x := regionToCode(r)
|
||
|
i := sort.Search(len(regionToCurrency), func(i int) bool {
|
||
|
return regionToCurrency[i].region >= x
|
||
|
})
|
||
|
if i < len(regionToCurrency) && regionToCurrency[i].region == x {
|
||
|
return Unit{regionToCurrency[i].code}, true
|
||
|
}
|
||
|
return Unit{}, false
|
||
|
}
|
||
|
|
||
|
// FromTag reports the most likely currency for the given tag. It considers the
|
||
|
// currency defined in the -u extension and infers the region if necessary.
|
||
|
func FromTag(t language.Tag) (Unit, language.Confidence) {
|
||
|
if cur := t.TypeForKey("cu"); len(cur) == 3 {
|
||
|
c, _ := ParseISO(cur)
|
||
|
return c, language.Exact
|
||
|
}
|
||
|
r, conf := t.Region()
|
||
|
if cur, ok := FromRegion(r); ok {
|
||
|
return cur, conf
|
||
|
}
|
||
|
return Unit{}, language.No
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
// Undefined and testing.
|
||
|
XXX Unit = Unit{}
|
||
|
XTS Unit = Unit{xts}
|
||
|
|
||
|
// G10 currencies https://en.wikipedia.org/wiki/G10_currencies.
|
||
|
USD Unit = Unit{usd}
|
||
|
EUR Unit = Unit{eur}
|
||
|
JPY Unit = Unit{jpy}
|
||
|
GBP Unit = Unit{gbp}
|
||
|
CHF Unit = Unit{chf}
|
||
|
AUD Unit = Unit{aud}
|
||
|
NZD Unit = Unit{nzd}
|
||
|
CAD Unit = Unit{cad}
|
||
|
SEK Unit = Unit{sek}
|
||
|
NOK Unit = Unit{nok}
|
||
|
|
||
|
// Additional common currencies as defined by CLDR.
|
||
|
BRL Unit = Unit{brl}
|
||
|
CNY Unit = Unit{cny}
|
||
|
DKK Unit = Unit{dkk}
|
||
|
INR Unit = Unit{inr}
|
||
|
RUB Unit = Unit{rub}
|
||
|
HKD Unit = Unit{hkd}
|
||
|
IDR Unit = Unit{idr}
|
||
|
KRW Unit = Unit{krw}
|
||
|
MXN Unit = Unit{mxn}
|
||
|
PLN Unit = Unit{pln}
|
||
|
SAR Unit = Unit{sar}
|
||
|
THB Unit = Unit{thb}
|
||
|
TRY Unit = Unit{try}
|
||
|
TWD Unit = Unit{twd}
|
||
|
ZAR Unit = Unit{zar}
|
||
|
|
||
|
// Precious metals.
|
||
|
XAG Unit = Unit{xag}
|
||
|
XAU Unit = Unit{xau}
|
||
|
XPT Unit = Unit{xpt}
|
||
|
XPD Unit = Unit{xpd}
|
||
|
)
|