diff --git a/UnitsNet.Tests/QuantityFormatterTests.cs b/UnitsNet.Tests/QuantityFormatterTests.cs
new file mode 100644
index 0000000000..9a2a948ca1
--- /dev/null
+++ b/UnitsNet.Tests/QuantityFormatterTests.cs
@@ -0,0 +1,139 @@
+// Licensed under MIT No Attribution, see LICENSE file at the root.
+// Copyright 2013 Andreas Gullberg Larsen (andreas.larsen84@gmail.com). Maintained at https://github.com/angularsen/UnitsNet.
+
+using Xunit;
+
+namespace UnitsNet.Tests
+{
+ public class QuantityFormatterTests
+ {
+ [Theory]
+ [InlineData("C")]
+ [InlineData("C0")]
+ [InlineData("C1")]
+ [InlineData("C2")]
+ [InlineData("C3")]
+ [InlineData("C4")]
+ [InlineData("C5")]
+ [InlineData("C6")]
+ [InlineData("c")]
+ [InlineData("c0")]
+ [InlineData("c1")]
+ [InlineData("c2")]
+ [InlineData("c3")]
+ [InlineData("c4")]
+ [InlineData("c5")]
+ [InlineData("c6")]
+ [InlineData("E")]
+ [InlineData("E0")]
+ [InlineData("E1")]
+ [InlineData("E2")]
+ [InlineData("E3")]
+ [InlineData("E4")]
+ [InlineData("E5")]
+ [InlineData("E6")]
+ [InlineData("e")]
+ [InlineData("e0")]
+ [InlineData("e1")]
+ [InlineData("e2")]
+ [InlineData("e3")]
+ [InlineData("e4")]
+ [InlineData("e5")]
+ [InlineData("e6")]
+ [InlineData("F")]
+ [InlineData("F0")]
+ [InlineData("F1")]
+ [InlineData("F2")]
+ [InlineData("F3")]
+ [InlineData("F4")]
+ [InlineData("F5")]
+ [InlineData("F6")]
+ [InlineData("f")]
+ [InlineData("f0")]
+ [InlineData("f1")]
+ [InlineData("f2")]
+ [InlineData("f3")]
+ [InlineData("f4")]
+ [InlineData("f5")]
+ [InlineData("f6")]
+ [InlineData("N")]
+ [InlineData("N0")]
+ [InlineData("N1")]
+ [InlineData("N2")]
+ [InlineData("N3")]
+ [InlineData("N4")]
+ [InlineData("N5")]
+ [InlineData("N6")]
+ [InlineData("n")]
+ [InlineData("n0")]
+ [InlineData("n1")]
+ [InlineData("n2")]
+ [InlineData("n3")]
+ [InlineData("n4")]
+ [InlineData("n5")]
+ [InlineData("n6")]
+ [InlineData("P")]
+ [InlineData("P0")]
+ [InlineData("P1")]
+ [InlineData("P2")]
+ [InlineData("P3")]
+ [InlineData("P4")]
+ [InlineData("P5")]
+ [InlineData("P6")]
+ [InlineData("p")]
+ [InlineData("p0")]
+ [InlineData("p1")]
+ [InlineData("p2")]
+ [InlineData("p3")]
+ [InlineData("p4")]
+ [InlineData("p5")]
+ [InlineData("p6")]
+ [InlineData("R")]
+ [InlineData("R0")]
+ [InlineData("R1")]
+ [InlineData("R2")]
+ [InlineData("R3")]
+ [InlineData("R4")]
+ [InlineData("R5")]
+ [InlineData("R6")]
+ [InlineData("r")]
+ [InlineData("r0")]
+ [InlineData("r1")]
+ [InlineData("r2")]
+ [InlineData("r3")]
+ [InlineData("r4")]
+ [InlineData("r5")]
+ [InlineData("r6")]
+ public static void StandardNumericFormatStrings_Equals_ValueWithFormatStringAndAbbreviation(string format)
+ {
+ var length = Length.FromMeters(123456789.987654321);
+
+ var expected = string.Format($"{{0:{format}}} {{1:a}}", length.Value, length);
+ Assert.Equal(expected, QuantityFormatter.Format(length, format));
+ }
+
+ [Theory]
+ [InlineData("000")]
+ [InlineData("0.00")]
+ [InlineData("#####")]
+ [InlineData("#.##")]
+ [InlineData("##,#")]
+ [InlineData("#,#,,")]
+ [InlineData("%#0.00")]
+ [InlineData("##.0 %")]
+ [InlineData("#0.00‰")]
+ [InlineData("#0.0e0")]
+ [InlineData("0.0##e+00")]
+ [InlineData("0.0e+00")]
+ [InlineData(@"\###00\#")]
+ [InlineData("#0.0#;(#0.0#);-\0-")]
+ [InlineData("#0.0#;(#0.0#)")]
+ public static void CustomNumericFormatStrings_Equals_ValueWithFormatStringAndAbbreviation(string format)
+ {
+ var length = Length.FromMeters(123456789.987654321);
+
+ var expected = string.Format($"{{0:{format}}} {{1:a}}", length.Value, length);
+ Assert.Equal(expected, QuantityFormatter.Format(length, format));
+ }
+ }
+}
diff --git a/UnitsNet/QuantityFormatter.cs b/UnitsNet/QuantityFormatter.cs
index a949f3b3d9..3a774b4427 100644
--- a/UnitsNet/QuantityFormatter.cs
+++ b/UnitsNet/QuantityFormatter.cs
@@ -14,7 +14,66 @@ namespace UnitsNet
public class QuantityFormatter
{
///
- /// Formats the given quantity using the given format string and format provider.
+ /// The available UnitsNet custom format specifiers.
+ ///
+ private static readonly char[] UnitsNetFormatSpecifiers = { 'A', 'a', 'G', 'g', 'Q', 'q', 'S', 's', 'U', 'u', 'V', 'v' };
+
+ ///
+ /// Formats a quantity using the given format string and format provider.
+ ///
+ /// The quantity's unit type, for example .
+ /// The quantity to format.
+ /// The format string.
+ ///
+ /// The valid format strings are as follows:
+ ///
+ /// -
+ /// A standard numeric format string.
+ /// Any of the standard numeric format for except for "G" or "g".
+ /// "C" or "c", "E" or "e", "F" or "f", "N" or "n", "P" or "p", "R" or "r" are all accepted.
+ ///
+ ///
+ /// -
+ /// "G" or "g".
+ /// The value with 2 significant digits after the radix followed by the unit abbreviation, such as "1.23 m".
+ ///
+ /// -
+ /// "A" or "a".
+ /// The default unit abbreviation for , such as "m".
+ ///
+ /// -
+ /// "A0", "A1", ..., "An" or "a0", "a1", ..., "an".
+ /// The n-th unit abbreviation for . "a0" is the same as "a".
+ /// A will be thrown if the requested abbreviation index does not exist.
+ ///
+ /// -
+ /// "V" or "v".
+ /// The string representation of using the default ToString method.
+ ///
+ /// -
+ /// "U" or "u".
+ /// The enum name of , such as "Meter".
+ ///
+ /// -
+ /// "Q" or "q".
+ /// The quantity name, such as "Length".
+ ///
+ /// -
+ /// "S1", "S2", ..., "Sn" or "s1", "s2", ..., "sn".
+ /// The value with n significant digits after the radix followed by the unit abbreviation. For example,
+ /// "s4" would return "1.2345 m" if is 1.2345678. Trailing zeros are omitted.
+ ///
+ ///
+ ///
+ /// The string representation.
+ public static string Format(IQuantity quantity, string format)
+ where TUnitType : Enum
+ {
+ return Format(quantity, format, CultureInfo.CurrentUICulture);
+ }
+
+ ///
+ /// Formats a quantity using the given format string and format provider.
///
/// The quantity's unit type, for example .
/// The quantity to format.
@@ -23,57 +82,108 @@ public class QuantityFormatter
/// if null.
///
/// The valid format strings are as follows:
- /// "g": The value with 2 significant digits after the radix followed by the unit abbreviation, such as "1.23 m".
- /// "a": The default unit abbreviation for , such as "m".
- /// "a0", "a1", ..., "aN": The Nth unit abbreviation for . "a0" is the same as "a".
- /// A will be thrown if the requested abbreviation index does not exist.
- /// "v": String representation of .
- /// "u": The enum name of , such as "Meter".
- /// "q": The quantity name, such as "Length".
- /// "s1", "s2", ..., "sN": The value with N significant digits after the radix followed by the unit abbreviation. For example,
- /// "s4" would return "1.2345 m" if is 1.2345678. Trailing zeros are omitted.
+ ///
+ /// -
+ /// A standard numeric format string.
+ /// Any of the standard numeric format for except for "G" or "g".
+ /// "C" or "c", "E" or "e", "F" or "f", "N" or "n", "P" or "p", "R" or "r" are all accepted.
+ ///
+ ///
+ /// -
+ /// "G" or "g".
+ /// The value with 2 significant digits after the radix followed by the unit abbreviation, such as "1.23 m".
+ ///
+ /// -
+ /// "A" or "a".
+ /// The default unit abbreviation for , such as "m".
+ ///
+ /// -
+ /// "A0", "A1", ..., "An" or "a0", "a1", ..., "an".
+ /// The n-th unit abbreviation for . "a0" is the same as "a".
+ /// A will be thrown if the requested abbreviation index does not exist.
+ ///
+ /// -
+ /// "V" or "v".
+ /// The string representation of using the default ToString method.
+ ///
+ /// -
+ /// "U" or "u".
+ /// The enum name of , such as "Meter".
+ ///
+ /// -
+ /// "Q" or "q".
+ /// The quantity name, such as "Length".
+ ///
+ /// -
+ /// "S1", "S2", ..., "Sn" or "s1", "s2", ..., "sn".
+ /// The value with n significant digits after the radix followed by the unit abbreviation. For example,
+ /// "s4" would return "1.2345 m" if is 1.2345678. Trailing zeros are omitted.
+ ///
+ ///
///
/// The string representation.
public static string Format(IQuantity quantity, string format, IFormatProvider? formatProvider)
where TUnitType : Enum
{
- formatProvider = formatProvider ?? CultureInfo.CurrentUICulture;
+ formatProvider ??= CultureInfo.CurrentUICulture;
- var number = 0;
- var formatString = format;
+ if(string.IsNullOrWhiteSpace(format))
+ format = "g";
- if(string.IsNullOrEmpty(formatString))
- formatString = "g";
+ char formatSpecifier = format[0];
- if(formatString.StartsWith("a") || formatString.StartsWith("s"))
+ if(UnitsNetFormatSpecifiers.Any(unitsNetFormatSpecifier => unitsNetFormatSpecifier == formatSpecifier))
{
- if(formatString.Length > 1 && !int.TryParse(formatString.Substring(1), out number))
- throw new FormatException($"The {format} format string is not supported.");
+ // UnitsNet custom format string
- formatString = formatString.Substring(0, 1);
- }
+ int precisionSpecifier = 0;
- switch(formatString)
- {
- case "g":
- return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, 2);
- case "a":
- var abbreviations = UnitAbbreviationsCache.Default.GetUnitAbbreviations(quantity.Unit, formatProvider);
+ switch(formatSpecifier)
+ {
+ case 'A':
+ case 'a':
+ case 'S':
+ case 's':
+ if(format.Length > 1 && !int.TryParse(format.Substring(1), out precisionSpecifier))
+ throw new FormatException($"The {format} format string is not supported.");
+ break;
+ }
+
+ switch(formatSpecifier)
+ {
+ case 'G':
+ case 'g':
+ return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, 2);
+ case 'A':
+ case 'a':
+ var abbreviations = UnitAbbreviationsCache.Default.GetUnitAbbreviations(quantity.Unit, formatProvider);
+
+ if(precisionSpecifier >= abbreviations.Length)
+ throw new FormatException($"The {format} format string is invalid because the abbreviation index does not exist.");
- if(number >= abbreviations.Length)
- throw new FormatException($"The {format} format string is invalid because the abbreviation index does not exist.");
+ return abbreviations[precisionSpecifier];
+ case 'V':
+ case 'v':
+ return quantity.Value.ToString(formatProvider);
+ case 'U':
+ case 'u':
+ return quantity.Unit.ToString();
+ case 'Q':
+ case 'q':
+ return quantity.QuantityInfo.Name;
+ case 'S':
+ case 's':
+ return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, precisionSpecifier);
+ default:
+ throw new FormatException($"The {format} format string is not supported.");
+ }
+ }
+ else
+ {
+ // Anything else is a standard numeric format string with default unit abbreviation postfix.
- return abbreviations[number];
- case "v":
- return quantity.Value.ToString(formatProvider);
- case "u":
- return quantity.Unit.ToString();
- case "q":
- return quantity.QuantityInfo.Name;
- case "s":
- return ToStringWithSignificantDigitsAfterRadix(quantity, formatProvider, number);
- default:
- throw new FormatException($"The {format} format string is not supported.");
+ var abbreviations = UnitAbbreviationsCache.Default.GetUnitAbbreviations(quantity.Unit, formatProvider);
+ return string.Format(formatProvider, $"{{0:{format}}} {{1}}", quantity.Value, abbreviations.First());
}
}