Skip to content

radical-app/money

Repository files navigation

GoLang Money

GoLang library to make working with money safer, easier, and fun!

golang money - No dependencies
- No anaemic model
- JSON formatter
- SQL driver
- Localized formatter(s)
Money Value Object
"If I had a dime for every time I've seen someone use FLOAT to store currency, I'd have $999.997634"
-- Bill Karwin

In short: You shouldn't represent monetary values by a float.
Wherever you need to represent money, use this Money value object.
package main

import (
    "fmt"
    "github.com/radical-app/money"
)

func main() {
    fiveEur := money.EUR(500) // see list of all currencies
    tenEur, err := fiveEur.Add(fiveEur)
    fmt.Print(err)
    
    zeroEur, err := tenEur.Subtract(tenEur)
    fmt.Print(err)
    
    zeroEur.IsZero() // true
    
    anotherFiveEur,err := zeroEur.Add(fiveEur)
    fmt.Print(err)
    
    fiveEur.IsEquals(anotherFiveEur) // true

    fmt.Print(fiveEur.String()) // EUR 500 for beautiful formatter see below 
}

.Forge and .Parse

Money from Int and only if you really-really-really need ...Forge from Float

usd312 := money.USD(312)
usd312, err := money.Forge(312, "USD")

usd312 := money.FloatUSD(3.12)
usd312, err := money.ForgeFloat(3.12, "USD")

More .Parse() from string

.Parse() is the opposite of .String()

usd312, err := money.Parse("USD 312")
usd312.String() // "USD 312"

eur312, err := money.ParseWithFallback("312", "EUR")
 
// this uses EUR because the string has it   
eur312, err := money.ParseWithFallback("EUR 312", "JPY")

// not suggested solution use ParseWithFallback if you have to deal with multiple currencies
money.DefaultCurrencyCode="JPY"
jpy312, err := money.Parse("312")

example at parse_test.go

Marshal/UnMarshal Custom Formatter

Custom Marshaller from and to Json

json.Marshal(money.EUR(123))

will produce the simplified json for money.DTO:

{"amount":123,"currency":"EUR","symbol":"","cents":100}

and

m := &Money{}
json.Unmarshal([]byte('{"amount":123,"currency":"EUR","symbol":"€","cents":100}'), m)

will produce the money.EUR(123)

example at marshal_test.go

.String()

money.EUR(123).String() // "EUR 123"

.Display() beautiful money depending based on locale

import "github.com/radical-app/money/moneyfmt"
import "github.com/radical-app/money"

moneyfmt.Display(money.EUR(123400), "ru") // € 1 234
moneyfmt.Display(money.EUR(123456), "ru") // € 1 234,56

moneyfmt.Display(money.EUR(123456), "it") // € 1.234,56
moneyfmt.Display(money.EUR(123400), "it") // € 1.234

moneyfmt.Display(money.EUR(123456), "en") // € 1,234.56
moneyfmt.Display(money.EUR(123456), "jp") // € 1,234.56
moneyfmt.Display(money.EUR(123456), "zh") // € 1,234.56

example at moneyfmt/moneyfmt_test.go

SQL custom field support driver

Is possible to use in mysql the field as int or varchar or if you really really need decimal(13,4)

_, err := db.Exec("insert into blablabla int, string, decimal (?,?,?)",
  money.EUR(123).Int64(),
  money.EUR(123).String(),
  money.EUR(123).Float()
)  

and the Scan during a select is auto-magically done:

rows.Scan(&moneyStoredAsInt64,
  &moneyStoredAsString,
  &moneyStoredAsFloat
)

Real example: driver_integration_test.go

Limit

The biggest amount you can store in is 92.233.720.368.547.758,07 the math.MaxInt64 / currency.cents