GoMoney provides ability to work with monetary value using a currency's smallest unit.

Overview

Money

alt text

Go Report Card Coverage Status GoDoc License: MIT

GoMoney provides ability to work with monetary value using a currency's smallest unit. This package provides basic and precise Money operations such as rounding, splitting and allocating. Monetary values should not be stored as floats due to small rounding differences.

package main

import "github.com/Rhymond/go-money"

func main() {
    pound := money.New(100, money.GBP)
    twoPounds, err := pound.Add(pound)

    if err != nil {
        log.Fatal(err)
    }

    parties, err := twoPounds.Split(3)

    if err != nil {
        log.Fatal(err)
    }

    parties[0].Display() // £0.67
    parties[1].Display() // £0.67
    parties[2].Display() // £0.66
}

Quick start

Get the package:

$ go get github.com/Rhymond/go-money

Features

  • Provides a Money struct which stores information about an Money amount value and its currency.
  • Provides a Money.Amount struct which encapsulates all information about a monetary unit.
  • Represents monetary values as integers, in cents. This avoids floating point rounding errors.
  • Represents currency as Money.Currency instances providing a high level of flexibility.

Usage

Initialization

Initialize Money by using smallest unit value (e.g 100 represents 1 pound). Use ISO 4217 Currency Code to set money Currency. Note that constants are also provided for all ISO 4217 currency codes.

pound := money.New(100, money.GBP)

Comparison

Go-money provides base compare operations like:

  • Equals
  • GreaterThan
  • GreaterThanOrEqual
  • LessThan
  • LessThanOrEqual

Comparisons must be made between the same currency units.

pound := money.New(100, money.GBP)
twoPounds := money.New(200, money.GBP)
twoEuros := money.New(200, money.EUR)

pound.GreaterThan(twoPounds) // false, nil
pound.LessThan(twoPounds) // true, nil
twoPounds.Equals(twoEuros) // false, error: Currencies don't match

Asserts

  • IsZero
  • IsNegative
  • IsPositive

Zero value

To assert if Money value is equal to zero use IsZero()

pound := money.New(100, money.GBP)
result := pound.IsZero(pound) // false

Positive value

To assert if Money value is more than zero use IsPositive()

pound := money.New(100, money.GBP)
pound.IsPositive(pound) // true

Negative value

To assert if Money value is less than zero use IsNegative()

pound := money.New(100, money.GBP)
pound.IsNegative(pound) // false

Operations

  • Add
  • Subtract
  • Multiply
  • Absolute
  • Negative

Comparisons must be made between the same currency units.

Addition

Additions can be performed using Add().

pound := money.New(100, money.GBP)
twoPounds := money.New(200, money.GBP)

result, err := pound.Add(twoPounds) // £3.00, nil

Subtraction

Subtraction can be performed using Subtract().

pound := money.New(100, money.GBP)
twoPounds := money.New(200, money.GBP)

result, err := pound.Subtract(twoPounds) // -£1.00, nil

Multiplication

Multiplication can be performed using Multiply().

pound := money.New(100, money.GBP)

result := pound.Multiply(2) // £2.00

Absolute

Return absolute value of Money structure

pound := money.New(-100, money.GBP)

result := pound.Absolute() // £1.00

Negative

Return negative value of Money structure

pound := money.New(100, money.GBP)

result := pound.Negative() // -£1.00

Allocation

  • Split
  • Allocate

Splitting

In order to split Money for parties without losing any pennies due to rounding differences, use Split().

After division leftover pennies will be distributed round-robin amongst the parties. This means that parties listed first will likely receive more pennies than ones that are listed later.

pound := money.New(100, money.GBP)
parties, err := pound.Split(3)

if err != nil {
    log.Fatal(err)
}

parties[0].Display() // £0.34
parties[1].Display() // £0.33
parties[2].Display() // £0.33

Allocation

To perform allocation operation use Allocate().

It splits money using the given ratios without losing pennies and as Split operations distributes leftover pennies amongst the parties with round-robin principle.

pound := money.New(100, money.GBP)
// Allocate is variadic function which can receive ratios as
// slice (int[]{33, 33, 33}...) or separated by a comma integers
parties, err := pound.Allocate(33, 33, 33)

if err != nil {
    log.Fatal(err)
}

parties[0].Display() // £0.34
parties[1].Display() // £0.33
parties[2].Display() // £0.33

Format

To format and return Money as a string use Display().

money.New(123456789, money.EUR).Display() // €1,234,567.89

To format and return Money as a float64 representing the amount value in the currency's subunit use AsMajorUnits().

money.New(123456789, money.EUR).AsMajorUnits() // 1234567.89

Contributing

Thank you for considering contributing! Please use GitHub issues and Pull Requests for contributing.

License

The MIT License (MIT). Please see License File for more information.

forthebadge

Comments
  • Multiply and Divide only accept an int

    Multiply and Divide only accept an int

    Multiply and Divide only accept an int. What was the reason for this? It seems to lessen the usefulness of these functions. For example, calculating the net value from gross is impossible.

    gross := money.New(599, "GBP")
    net := gross.Divide(1.2) // error
    tax, _ := gross.Subtract(net)
    

    or am I missing something and this is possible in another way?

    opened by nomad-software 16
  • Add JSON Marshal/Unmarshal

    Add JSON Marshal/Unmarshal

    @Rhymond I've waited to be merged #38, but it looks have not updated long time. I need JSON marshaling/unmarshaling feature for go-money. I've tried to update and fix it. Could you check this PR?

    opened by k315k1010 11
  • Refactor for abstraction

    Refactor for abstraction

    Fix #3 Address #6

    • Amount struct with conversion methods and hidden internals
    • Remove all pointers and dereferences. Unneeded. This should be faster (stay on stack)
    • Remove calculator file and move all calculations to new Amount object

    Due to the Amount struct hiding the internals, we can change the implementation to a Big implementation in the future, if necessary, without impacting existing code. We would simply add a Big() method to return the internal as a Big, as well as alter how the toAmount function parses the incoming values.

    opened by tylerstillwater 11
  • Proposal: Add function NewFromFloat

    Proposal: Add function NewFromFloat

    It would be really useful to be able to create a new Money struct from a float or maybe a decimal string ('1.243'). Formatting the decimal amount from a source (API response, file, ...) to a Money struct can get really tedious, that's why I propose a util function inside the money package for dealing with this cases.

    This is my current implementation:

    func NewFromFloat(amount float64, currency string) *money.Money {
    	currencyDecimals := math.Pow(10, float64(money.GetCurrency(currency).Fraction))
    	amountCents := int64(amount * currencyDecimals)
    	return money.New(amountCents, currency)
    }
    

    To not lose precision dealing with floats there can also be a NewFromString, but I haven gotten around to implementing it.

    What do you guys think?

    opened by npinochet 6
  • Question about the rounding for NewFromFloat

    Question about the rounding for NewFromFloat

    Love the new addition of NewFromFloat. Just wanted to get a clearer understanding of always rounding trailing decimals down when given.

     cents := money.NewFromFloat(99.999999, money.USD)
     //->  cents: 9999
    

    Just want to know the why between getting 9999 vs 10000?

    Thanks!

    opened by TimothyIp 5
  • Find Currency by numeric code

    Find Currency by numeric code

    Hello, I'm adding the option to search Currencies by its numeric code ISO number. This helps because many implementations works around the iso numerical code numbers (like Visa and Mastercard for instance).

    • This code will load a second map of currencies but now this map uses the numerical ISO code as key of the map.
    • The reason to have another map is to avoid having a loop each time we need to access a currency its iso numerical code
    • Added idea and ds_store files to git ignore
    opened by Stocco 5
  • Addition of currency code constants

    Addition of currency code constants

    See #72 for the original proposal.

    This pull request includes the addition of constants for the 168 ISO 4217 currency codes that are currently present in this package.

    In addition to adding these, I have updated the map of currencies in currency.go for both the key in the map and the currency code in the struct to be returned. Similarly, I have updated the tests to include their usage and updated the examples in the README to include their usage.

    Definitely willing to have a discussion about how we include their usage in the README. This is certainly the part of this pull request that I have the most questions about so far. In particular, I worry about causing confusion with the usage of the constants with regards to a consumer of the package adding their own currencies.

    opened by jamesallured 5
  • Use of int causes overflows and arch-specific variability

    Use of int causes overflows and arch-specific variability

    Using the architecture-independent int type will cause 32-bit binaries to overflow if more than 21,474,836.47 is used in a base-100 currency, introducing architecture-dependent behaviour differences which may not be caught.

    This can be resolved by switching amounts to use the int64 data type. This will still work on 32-bit architectures, at the slight expense of performance, and allow base-100 currency values up to an amount of 92,233,720,368,547,758.07 ... More than likely enough for most consumers of this package :)

    opened by bbrks 5
  • Get Currency by Numeric Code

    Get Currency by Numeric Code

    Code changes allow to get the currency by numeric code or a code used to index currencies map. The PR fixes issues with the PR https://github.com/Rhymond/go-money/pull/96

    opened by Rhymond 4
  • go get fails

    go get fails

    Running go get github.com/rhymond/go-money fails and spits out the message:

    go: github.com/rhymond/[email protected]: parsing go.mod: unexpected module path "github.com/Rhymond/go-money"
    go: error loading module requirements
    

    seems to be related to https://github.com/golang/go/issues/27154

    opened by logitick 4
  • Does not have i18n for formatting ?

    Does not have i18n for formatting ?

    Am building a templating system and need to handle currency properly. Does this library have functionality for this ? E.g in Sweden and Germany money is displayed differently

    If you know of I lib I can combine and PR if you want too

    opened by ghost 4
  • Digit separation in INR amount

    Digit separation in INR amount

    Hi,

    When using INR currency format, we are not getting the desired result:

    Result:

    1000.00 -> ₹1,000.00 (As expected) 10000.00 -> ₹10,000.00 (As expected) 100000.00 -> ₹100,000.00 (Expected ₹1,00,000.00) 1000000.00 -> ₹1,000,000.00 (Expected ₹10,00,000.00)

    and so on

    Can you provide a solution for this since this is regarding the placement of separator not the value of separator?

    opened by amitesh-furlenco 0
  • fixed NewFromFloat to calculate accurate values

    fixed NewFromFloat to calculate accurate values

    Multiplying floats by primitive types is not precise enough, and there is a serious bug that the amount generated by NewFromFloat is different from the original value. issue : https://github.com/Rhymond/go-money/issues/121, #124

    Fixed to use "github.com/shopspring/decimal" for float multiplication.

    This fix requires a dependency on an external module,, but I could find no other idea.

    opened by kotaroyamazaki 1
  • Fixed GetCurrency to be case-insensitive

    Fixed GetCurrency to be case-insensitive

    ~~NewFromFloat did not support lowercase currency codes because it used getCurrencyByCode. As same as New(), fix to use newCurrency(code).get() instead.~~

    Fixed GetCurrency to be case-insensitive so that it wouldn't fail at upper case.

    And Add test case.

    opened by kotaroyamazaki 0
  • NewFromFloat trouble

    NewFromFloat trouble

    Why is the float64 value equal to 1.15 created in the money type as 1.14? The link below shows an example of the supposed error: https://go.dev/play/p/Z64XrP8LdTc

    opened by font1k 3
Releases(v1.0.9)
Owner
Raymond
In order to understand recursion, you must first understand recursion
Raymond
log4jScanner: provides you with the ability to scan internal (only) subnets for vulnerable log4j web servicelog4jScanner: provides you with the ability to scan internal (only) subnets for vulnerable log4j web service

log4jScanner Goals This tool provides you with the ability to scan internal (only) subnets for vulnerable log4j web services. It will attempt to send

Profero 482 Jan 5, 2023
Utility package that provides the ability to more conveniently work with URL parameters.

Utility package that provides the ability to more conveniently work with URL parameters.

Radik Khisamutdinov 1 Feb 8, 2022
Go compiler made from scratch, which can compile itself. It's going to be the smallest and simplest go compiler in the world.

Babygo, a go compiler made from scratch Babygo is a small and simple go compiler. (Smallest and simplest in the world, I believe.) It is made from scr

DQNEO 231 Jan 8, 2023
Nipo is a powerful, fast, multi-thread, clustered and in-memory key-value database, with ability to configure token and acl on commands and key-regexes written by GO

Welcome to NIPO Nipo is a powerful, fast, multi-thread, clustered and in-memory key-value database, with ability to configure token and acl on command

Morteza Bashsiz 17 Dec 28, 2022
PingMe is a CLI tool which provides the ability to send messages or alerts to multiple messaging platforms & email.

PingMe is a personal project to satisfy my needs of having alerts, most major platforms have integration to send alerts but its not always useful, either you are stuck with one particular platform, or you have to do alot of integrations. I needed a small app which i can just call from my backup scripts, cron jobs, CI/CD pipelines or from anywhere to send a message with particular information. And i can ship it everywhere with ease. Hence, the birth of PingMe.

Khaliq 585 Dec 28, 2022
Minutes is a CLI tool for synchronizing work logs between multiple time trackers, invoicing, and bookkeeping software to make entrepreneurs' daily work easier.

Minutes is a CLI tool for synchronizing work logs between multiple time trackers, invoicing, and bookkeeping software to make entrepreneurs' daily work easier.

Gábor Boros 21 Aug 8, 2022
onnx-go gives the ability to import a pre-trained neural network within Go without being linked to a framework or library.

This is a Go Interface to Open Neural Network Exchange (ONNX). Overview onnx-go contains primitives to decode a onnx binary model into a computation b

Olivier Wulveryck 456 Dec 24, 2022
Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Gowl is a process management and process monitoring tool at once. An infinite worker pool gives you the ability to control the pool and processes and monitor their status.

Hamed Yousefi 40 Nov 10, 2022
`ctxio` gives `io.copy` operations the ability to cancel with context and retrieve progress data.

ctxio The ctxio package gives golang io.copy operations the ability to terminate with context and retrieve progress data. Install go get github.com/

Binalyze 12 Aug 10, 2022
GitHub CLI extension to clone (or update) all repositories in an Organization, with the ability to filter via search queries.

gh-org-repo-sync GitHub CLI extension to clone all repositories in an Organization, with the ability to filter via search queries. If a local clone al

Armel Soro 12 Nov 2, 2022
Impress your friends with your ability to maybe solve the Wordle most of the time (probably)

wordle-assistant Impress your friends with your ability to maybe solve the Wordle most of the time (probably). This was coded as quickly and dirtily a

James Hogle 0 Feb 10, 2022
go-jsonc provides a way to work with commented json by converting it to plain json.

JSON with comments for GO Decodes a "commented json" to "json". Provided, the input must be a valid jsonc document. Supports io.Reader With this, we c

Akshay Bharambe 9 Apr 6, 2022
Package GoEagi provides some fundamental functionalities that work with Asterisk's EAGI

GoEagi Package GoEagi provides some fundamental functionalities that work with Asterisk's EAGI. It has the following features: Audio Streaming Google'

null 5 Dec 16, 2022
:steam_locomotive: Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. Dual Array and Full map support.

Package form Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values. It has the following features: Supports map of

Go Playgound 573 Dec 26, 2022
Traefik proxy plugin to extract HTTP header value and create a new header with extracted value

Copy header value Traefik plugin Traefik plugin that copies HTTP header value with format key1=value1; key2=value2 into a new header. Motivation for t

Argyle 3 May 26, 2022
Golang-key-value-store - Key Value Store API Service with Go DDD Architecture

This document specifies the tools used in the Key-Value store and reorganizes how to use them. In this created service, In-Memory Key-Value Service was created and how to use the API is specified in the HTML file in the folder named "doc"

Kemal Emre Ballı 2 Jul 31, 2022
go websocket client for unit testing of a websocket handler

wstest A websocket client for unit-testing a websocket server The gorilla organization provides full featured websocket implementation that the standa

Eyal Posener 98 Dec 21, 2022
Unit tests generator for Go programming language

GoUnit GoUnit is a commandline tool that generates tests stubs based on source function or method signature. There are plugins for Vim Emacs Atom Subl

Max Chechel 66 Jan 1, 2023
Easier way to unit test terraform

Unit testing terraform (WIP) Disclaimer Currently, the only way to compare values is using JSON query path and all types are strings. want := terraf

Thiago Nache Carvalho 51 Aug 16, 2022