varunsrin / rusty_money

Money library for Rust
MIT License
85 stars 32 forks source link

When parsing Euro amounts, amounts that should be valid throw an error #61

Open jgrantr opened 3 years ago

jgrantr commented 3 years ago

Here is a test case that will fail (and shouldn't, unless I am missing something):

#[test]
fn test_rusty_money() {
    let eur = Money::from_str("2.00", iso::EUR).unwrap();

}

Euro's are divided into 100 cents (much like the GBP and the USD), and yet the parser is treating an amount like "2.00" or "2.50" as invalid. If I change that line to have 3 digits after the decimal, it magically works, even though "2.00" and "2.000" are the same number. Seems like bug, but perhaps I am missing something.

jgrantr commented 3 years ago

What I was looking for here is: I have a number that represents a cost and I want to format it as currency. This works fine as long as it is an integer, but without having to re-create stuff, how do I take a generic floating-point number (like 2.5) and represent it as €2,50 or $2.50 or £2.50, etc?

korrat commented 2 years ago

The problem in this case is confusion between the group separator and the decimal separator. Euros use points (.) as group separator. That's why €2.000 works (and isn't actually the same as €2.00). To make it work, change your code example to

#[test]
fn test_rusty_money() {
    let eur = Money::from_str("2,00", iso::EUR).unwrap();

}

In contrast, GBP and USD use commas (,) as group separator and the point (.) as decimal separator, since they use a different locale internally.

NobbZ commented 2 years ago

@korrat I do not think that the seperators should be a matter of the currency, but a matter of surrounding context.

When I have a ledger and convert from USD to EUR (at a rate of 1:1 for simplicity) going from 1.00 USD to 1,00 EUR is just weird and confusing.

If though within the document punctuation is consistent, its much easier to understand, even for those who are not proficient with each currencies punctuation.


edit

Also the README does not confirm your claim, it shows an example where USD and EUR are parsed from a . string, and then printed in their localized versions.

use rusty-money::{Money, iso};
let usd = Money::from_str("-2000.009", iso::USD).unwrap();
let eur = Money::from_str("-2000.009", iso::EUR).unwrap();

println!("{}", usd);                                        // -$2,000.01
println!("{}", eur);                                        // -€2.000,01;
korrat commented 2 years ago

@NobbZ, I agree, parsing (and probably also formatting) should be independent of the currency.

The right way to go would probably be a locale-aware parser for Decimal, which rusty_money can then extend to parse amount strings.

Also, my previous statement was an oversimplification of the issue from my German-speaking perspective. There are at least 4 different ways in use today to format an amount in euros. I imagine that similar problems also affect other currencies.


Note that with the current version of rusty_money that README example runs fine, but the final line prints -€2.000.009,00, which is in line with using . for grouping.