76 lines
2.1 KiB
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)
|
|
}
|
|
}
|