inou/lib/parse_numeric.go

76 lines
2.1 KiB
Go

package lib
import (
"strconv"
"strings"
)
// ParseNumeric parses a numeric string that may use comma or period as
// decimal or thousands separator. Returns the float value and true on success.
//
// Rules:
// 1. Both comma and period → last one is decimal separator
// 2. Only one separator type → count digits after last occurrence:
// - 3 digits → ambiguous → hint decides (default: thousands)
// - other → decimal separator
// 3. No separators → plain integer
//
// Hint: locale like "en", "de", "nl". Empty = default (comma=thousands).
func ParseNumeric(s string, hint string) (float64, bool) {
s = strings.TrimSpace(s)
if s == "" {
return 0, false
}
s = strings.TrimPrefix(s, "+")
hasComma := strings.Contains(s, ",")
hasPeriod := strings.Contains(s, ".")
switch {
case hasComma && hasPeriod:
// Last separator is the decimal one
if strings.LastIndex(s, ",") > strings.LastIndex(s, ".") {
// "1.234,56" → comma is decimal
s = strings.ReplaceAll(s, ".", "")
s = strings.Replace(s, ",", ".", 1)
} else {
// "1,234.56" → period is decimal
s = strings.ReplaceAll(s, ",", "")
}
case hasComma:
last := strings.LastIndex(s, ",")
after := s[last+1:]
if len(after) == 3 && commaIsThousands(hint) {
// "1,234" in en context → thousands
s = strings.ReplaceAll(s, ",", "")
} else {
// "12,5" or "1,23" → decimal
s = strings.ReplaceAll(s, ",", ".")
}
case hasPeriod:
last := strings.LastIndex(s, ".")
after := s[last+1:]
if len(after) == 3 && !commaIsThousands(hint) {
// "1.234" in de context → thousands
s = strings.ReplaceAll(s, ".", "")
}
// else: period is decimal (default), leave as-is
}
v, err := strconv.ParseFloat(s, 64)
return v, err == nil
}
// commaIsThousands returns true if comma is used as thousands separator
// in the given locale (English convention). False for European convention.
func commaIsThousands(hint string) bool {
switch strings.ToLower(hint) {
case "de", "nl", "fr", "es", "it", "pt", "ru", "tr", "pl", "cs", "da", "sv", "nb", "fi":
return false
default:
return true // en, en-us, en-gb, ja, zh, ko, "" (default)
}
}