diff --git a/UnitsNet.Tests/QuantityFormatterTests.cs b/UnitsNet.Tests/QuantityFormatterTests.cs index d0c6ba02e3..45283c4cdd 100644 --- a/UnitsNet.Tests/QuantityFormatterTests.cs +++ b/UnitsNet.Tests/QuantityFormatterTests.cs @@ -196,5 +196,19 @@ public void Format_WithFormatParameter_FormatsWithCurrentCulture() var actual = QuantityFormatter.Format(length, "G"); Assert.Equal(expected, actual); } + + [Theory] + [InlineData("A+0", "m")] + [InlineData("S+1", "123.3 m")] + public void Format_CustomSpecifierSuffix_ParsesUsingInvariantCulture(string format, string expected) + { + var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone(); + culture.NumberFormat.PositiveSign = "!"; + using var cultureScope = new CultureScope(culture); + + var length = Length.FromMeters(123.321); + + Assert.Equal(expected, QuantityFormatter.Default.Format(length, format)); + } } } diff --git a/UnitsNet/QuantityFormatter.cs b/UnitsNet/QuantityFormatter.cs index af2ff1cf22..c448833d90 100644 --- a/UnitsNet/QuantityFormatter.cs +++ b/UnitsNet/QuantityFormatter.cs @@ -144,9 +144,9 @@ public string Format(TQuantity quantity, string? format = null, IForm switch (format[0]) { #if NET - case 'S' or 's' when int.TryParse(format.AsSpan(1), out var precisionSpecifier): + case 'S' or 's' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out var precisionSpecifier): return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, precisionSpecifier); - case 'A' or 'a' when int.TryParse(format.AsSpan(1), out var abbreviationIndex): + case 'A' or 'a' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out var abbreviationIndex): { IReadOnlyList abbreviations = _unitAbbreviations.GetUnitAbbreviations(quantity.UnitKey, formatProvider); @@ -157,14 +157,14 @@ public string Format(TQuantity quantity, string? format = null, IForm return abbreviations[abbreviationIndex]; } - case 'C' or 'c' when int.TryParse(format.AsSpan(1), out _): + case 'C' or 'c' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out _): throw new FormatException($"The \"{format}\" (currency) format is not supported."); - case 'P' or 'p' when int.TryParse(format.AsSpan(1), out _): + case 'P' or 'p' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out _): throw new FormatException($"The \"{format}\" (percent) format is not supported."); #else - case 'S' or 's' when int.TryParse(format.Substring(1), out var precisionSpecifier): + case 'S' or 's' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var precisionSpecifier): return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, precisionSpecifier); - case 'A' or 'a' when int.TryParse(format.Substring(1), out var abbreviationIndex): + case 'A' or 'a' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var abbreviationIndex): { IReadOnlyList abbreviations = _unitAbbreviations.GetUnitAbbreviations(quantity.UnitKey, formatProvider); @@ -175,9 +175,9 @@ public string Format(TQuantity quantity, string? format = null, IForm return abbreviations[abbreviationIndex]; } - case 'C' or 'c' when int.TryParse(format.Substring(1), out _): + case 'C' or 'c' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out _): throw new FormatException($"The \"{format}\" (currency) format is not supported."); - case 'P' or 'p' when int.TryParse(format.Substring(1), out _): + case 'P' or 'p' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out _): throw new FormatException($"The \"{format}\" (percent) format is not supported."); #endif }