golang货币金额处理与格式化插件库currency的使用

Golang货币金额处理与格式化插件库currency的使用

功能特性

  1. 所有货币代码、数字代码和小数位数
  2. 所有地区的货币符号和格式
  3. 国家映射(国家代码=>货币代码)
  4. Amount结构体,具有值语义(Fowler的Money模式)
  5. Formatter,用于格式化金额和解析格式化后的金额

基本使用示例

package main

import (
	"fmt"
	"github.com/bojanz/currency"
)

func main() {
	// 创建一个欧元金额
	amount, _ := currency.NewAmount("275.98", "EUR")
	
	// 金额乘以4
	total, _ := amount.Mul("4")

	// 创建法语地区格式化器
	locale := currency.NewLocale("fr")
	formatter := currency.NewFormatter(locale)
	fmt.Println(formatter.Format(total)) // 输出: 1 103,92 €

	// 将金额转换为伊朗里亚尔并以波斯语显示
	total, _ = total.Convert("IRR", "45.538")
	total = total.Round()
	locale = currency.NewLocale("fa")
	formatter = currency.NewFormatter(locale)
	fmt.Println(formatter.Format(total)) // 输出: ‎ریال ۵۰٬۲۷۰
}

设计目标

底层使用真正的十进制实现

货币金额不能是浮点数。存储整数小数单位(如2.99=>299)在多种货币或需要亚小数单位精度时会变得有问题。因此使用了cockroachdb/apd包提供的任意精度十进制类型。

智能过滤CLDR数据

使用了CLDR的"现代"子集,从约560个地区减少到约370个。

易于比较

Amount结构体可以通过内置的Equal()方法与google/go-cmp进行比较。

可与PostgreSQL复合类型一起使用

由于实现了driver.Valuer和sql.Scanner接口,使用pgx驱动的应用程序可以将金额存储在复合类型中。

PostgreSQL集成示例

数据库Schema

CREATE TYPE price AS (
   number NUMERIC,
   currency_code TEXT
);

CREATE TABLE products (
   id CHAR(26) PRIMARY KEY,
   name TEXT NOT NULL,
   price price NOT NULL,
   created_at TIMESTAMPTZ NOT NULL,
   updated_at TIMESTAMPTZ
);

Go结构体

type Product struct {
	ID          string
	Name        string
	Price       currency.Amount
	CreatedAt   time.Time
	UpdatedAt   time.Time
}

查询示例

p := Product{}
row := tx.QueryRow(ctx, `SELECT id, name, price, created_at, updated_at FROM products WHERE id = $1`, id)
err := row.Scan(&p.ID, &p.Name, &p.Price, &p.CreatedAt, &p.UpdatedAt)

这个currency库提供了完整的货币处理能力,包括创建金额、数学运算、货币转换和地区化格式化等功能,是处理国际化货币需求的理想选择。


更多关于golang货币金额处理与格式化插件库currency的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang货币金额处理与格式化插件库currency的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 货币金额处理与格式化:currency 库使用指南

在 Go 语言中处理货币金额时,使用专门的库可以避免浮点数精度问题并提供更好的格式化功能。currency 是一个优秀的 Go 库,专门用于货币金额的处理和格式化。

安装 currency 库

go get -u github.com/bojanz/currency

基本使用

1. 创建金额对象

package main

import (
	"fmt"
	"github.com/bojanz/currency"
)

func main() {
	// 从字符串创建金额
	amount, err := currency.NewAmount("1234.56", "USD")
	if err != nil {
		panic(err)
	}

	// 从整数创建金额 (以分为单位)
	amount2 := currency.NewAmountFromInt64(123456, "USD", 2) // 1234.56 USD

	fmt.Println(amount.String())  // 输出: 1234.56 USD
	fmt.Println(amount2.String()) // 输出: 1234.56 USD
}

2. 金额运算

func main() {
	a, _ := currency.NewAmount("100.50", "USD")
	b, _ := currency.NewAmount("50.25", "USD")

	// 加法
	sum, err := a.Add(b)
	if err != nil {
		panic(err)
	}
	fmt.Println("Sum:", sum) // 输出: Sum: 150.75 USD

	// 减法
	diff, err := a.Sub(b)
	if err != nil {
		panic(err)
	}
	fmt.Println("Difference:", diff) // 输出: Difference: 50.25 USD

	// 乘法 (乘以标量)
	product, err := a.Mul(2)
	if err != nil {
		panic(err)
	}
	fmt.Println("Product:", product) // 输出: Product: 201.00 USD

	// 除法 (除以标量)
	quotient, err := a.Div(2)
	if err != nil {
		panic(err)
	}
	fmt.Println("Quotient:", quotient) // 输出: Quotient: 50.25 USD
}

3. 金额比较

func main() {
	a, _ := currency.NewAmount("100.00", "USD")
	b, _ := currency.NewAmount("50.00", "USD")
	c, _ := currency.NewAmount("100.00", "USD")

	fmt.Println(a.Equal(b)) // false
	fmt.Println(a.Equal(c)) // true
	fmt.Println(a.GreaterThan(b)) // true
	fmt.Println(a.LessThan(b)) // false
}

高级功能

1. 格式化金额

func main() {
	amount, _ := currency.NewAmount("1234.56", "USD")
	
	// 创建格式化器
	formatter := currency.NewFormatter(amount)
	
	// 设置格式选项
	formatter.NoGrouping(false) // 使用千位分隔符
	formatter.SetSymbol(currency.SymbolShow) // 显示货币符号
	
	// 格式化输出
	fmt.Println(formatter.Format()) // 输出: $1,234.56
	
	// 不同地区的格式化
	locale := currency.NewLocale("de-DE")
	eurAmount, _ := currency.NewAmount("1234.56", "EUR")
	deFormatter := currency.NewFormatter(eurAmount, locale)
	fmt.Println(deFormatter.Format()) // 输出: 1.234,56 €
}

2. 货币转换

func main() {
	// 创建汇率提供者
	rates := currency.NewRates()
	
	// 添加汇率 (1 USD = 0.85 EUR)
	rates.SetRate("USD", "EUR", 0.85)
	
	// 转换金额
	usdAmount, _ := currency.NewAmount("100.00", "USD")
	eurAmount, err := rates.Convert(usdAmount, "EUR")
	if err != nil {
		panic(err)
	}
	
	fmt.Println(eurAmount) // 输出: 85.00 EUR
}

3. 处理不同货币

func main() {
	usd, _ := currency.NewAmount("100.00", "USD")
	eur, _ := currency.NewAmount("85.00", "EUR")
	
	// 检查货币是否相同
	if usd.CurrencyCode() == eur.CurrencyCode() {
		fmt.Println("Same currency")
	} else {
		fmt.Println("Different currencies") // 输出: Different currencies
	}
	
	// 转换为相同货币再比较
	rates := currency.NewRates()
	rates.SetRate("USD", "EUR", 0.85)
	
	eurConverted, _ := rates.Convert(usd, "EUR")
	if eurConverted.Equal(eur) {
		fmt.Println("Amounts are equal after conversion") // 输出: Amounts are equal after conversion
	}
}

最佳实践

  1. 始终使用 currency 库处理货币金额,避免使用 float64 或 float32,以防止精度问题。

  2. 尽早验证货币代码

    if !currency.IsValid("USDD") { // 注意: 错误的货币代码
        fmt.Println("Invalid currency code")
    }
    
  3. 处理运算错误

    result, err := amount1.Add(amount2)
    if err != nil {
        // 可能是货币不匹配或其他错误
        return fmt.Errorf("failed to add amounts: %w", err)
    }
    
  4. 考虑使用上下文保存格式化选项

    func formatForUser(amount currency.Amount, locale string) string {
        loc := currency.NewLocale(locale)
        formatter := currency.NewFormatter(amount, loc)
        formatter.NoGrouping(false)
        return formatter.Format()
    }
    

currency 库提供了强大而灵活的货币处理功能,适合电子商务、金融应用等需要精确货币计算的场景。通过合理使用这个库,可以避免常见的货币处理陷阱,并确保应用程序在全球范围内正确显示和处理货币金额。

回到顶部