Skip to content

Commit d761370

Browse files
authored
Parse quantity format suffixes invariantly (#1689)
Extracted from #1544 because format-string parsing is unrelated to QuantityValue and is a standalone correctness improvement for the existing formatter. Moving it out keeps the feature PR focused and lets this behavior ship independently. Changes: - Parse significant-digit, abbreviation-index, currency, and percent suffixes with invariant culture. - Apply the same parsing rules to the span-based and .NET Framework-compatible code paths. Tests: - Add Format_CustomSpecifierSuffix_ParsesUsingInvariantCulture for abbreviation and significant-digit formats.
1 parent a9c805b commit d761370

2 files changed

Lines changed: 22 additions & 8 deletions

File tree

UnitsNet.Tests/QuantityFormatterTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,5 +196,19 @@ public void Format_WithFormatParameter_FormatsWithCurrentCulture()
196196
var actual = QuantityFormatter.Format(length, "G");
197197
Assert.Equal(expected, actual);
198198
}
199+
200+
[Theory]
201+
[InlineData("A+0", "m")]
202+
[InlineData("S+1", "123.3 m")]
203+
public void Format_CustomSpecifierSuffix_ParsesUsingInvariantCulture(string format, string expected)
204+
{
205+
var culture = (CultureInfo)CultureInfo.InvariantCulture.Clone();
206+
culture.NumberFormat.PositiveSign = "!";
207+
using var cultureScope = new CultureScope(culture);
208+
209+
var length = Length.FromMeters(123.321);
210+
211+
Assert.Equal(expected, QuantityFormatter.Default.Format(length, format));
212+
}
199213
}
200214
}

UnitsNet/QuantityFormatter.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,9 @@ public string Format<TQuantity>(TQuantity quantity, string? format = null, IForm
144144
switch (format[0])
145145
{
146146
#if NET
147-
case 'S' or 's' when int.TryParse(format.AsSpan(1), out var precisionSpecifier):
147+
case 'S' or 's' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out var precisionSpecifier):
148148
return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, precisionSpecifier);
149-
case 'A' or 'a' when int.TryParse(format.AsSpan(1), out var abbreviationIndex):
149+
case 'A' or 'a' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out var abbreviationIndex):
150150
{
151151
IReadOnlyList<string> abbreviations = _unitAbbreviations.GetUnitAbbreviations(quantity.UnitKey, formatProvider);
152152

@@ -157,14 +157,14 @@ public string Format<TQuantity>(TQuantity quantity, string? format = null, IForm
157157

158158
return abbreviations[abbreviationIndex];
159159
}
160-
case 'C' or 'c' when int.TryParse(format.AsSpan(1), out _):
160+
case 'C' or 'c' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out _):
161161
throw new FormatException($"The \"{format}\" (currency) format is not supported.");
162-
case 'P' or 'p' when int.TryParse(format.AsSpan(1), out _):
162+
case 'P' or 'p' when int.TryParse(format.AsSpan(1), CultureInfo.InvariantCulture, out _):
163163
throw new FormatException($"The \"{format}\" (percent) format is not supported.");
164164
#else
165-
case 'S' or 's' when int.TryParse(format.Substring(1), out var precisionSpecifier):
165+
case 'S' or 's' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var precisionSpecifier):
166166
return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, precisionSpecifier);
167-
case 'A' or 'a' when int.TryParse(format.Substring(1), out var abbreviationIndex):
167+
case 'A' or 'a' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out var abbreviationIndex):
168168
{
169169
IReadOnlyList<string> abbreviations = _unitAbbreviations.GetUnitAbbreviations(quantity.UnitKey, formatProvider);
170170

@@ -175,9 +175,9 @@ public string Format<TQuantity>(TQuantity quantity, string? format = null, IForm
175175

176176
return abbreviations[abbreviationIndex];
177177
}
178-
case 'C' or 'c' when int.TryParse(format.Substring(1), out _):
178+
case 'C' or 'c' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out _):
179179
throw new FormatException($"The \"{format}\" (currency) format is not supported.");
180-
case 'P' or 'p' when int.TryParse(format.Substring(1), out _):
180+
case 'P' or 'p' when int.TryParse(format.Substring(1), NumberStyles.Integer, CultureInfo.InvariantCulture, out _):
181181
throw new FormatException($"The \"{format}\" (percent) format is not supported.");
182182
#endif
183183
}

0 commit comments

Comments
 (0)