Skip to content

Commit de98f17

Browse files
committed
Fixed formatting negative durations, rounding. Bump version
1 parent ba27ab2 commit de98f17

File tree

4 files changed

+18
-29
lines changed

4 files changed

+18
-29
lines changed

README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ Fraction | 0/0 |double|Convert.ToDouble()
4141
Exponent | \#0.0E+0 |double|Convert.ToDouble()
4242
Date/Time| hh\:mm |DateTime|Convert.ToDateTime()
4343
Duration | \[hh\]\:mm|TimeSpan|Cast to TimeSpan
44-
General | General |(any)|Convert.ToString()
44+
General | General |(any)|CompatibleConvert.ToString()
4545
Text | ;;;"Text: "@|string|Convert.ToString()
4646

47-
In case of errors, `Format()` returns the value from `Convert.ToString()`.
47+
In case of errors, `Format()` returns the value from `CompatibleConvert.ToString()`.
48+
49+
`CompatibleConvert.ToString()` formats floats and doubles with explicit precision, or falls back to `Convert.ToString()` for any other types.
4850

4951
## TODO/notes
5052

src/ExcelNumberFormat/ExcelNumberFormat.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<TargetFrameworks>net20;netstandard1.0;netstandard2.0</TargetFrameworks>
5-
<VersionPrefix>1.0.9</VersionPrefix>
5+
<VersionPrefix>1.0.10</VersionPrefix>
66
<GenerateDocumentationFile>true</GenerateDocumentationFile>
77
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
88
<Description>.NET library to parse ECMA-376 number format strings and format values like Excel and other spreadsheet softwares.</Description>

src/ExcelNumberFormat/Formatter.cs

+8-26
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
7777
// The timeSpan input is then truncated to the remainder fraction, which is used to format mm and/or ss.
7878
var result = new StringBuilder();
7979
var containsMilliseconds = false;
80-
for (var i = tokens.Count-1; i >= 0; i--)
80+
for (var i = tokens.Count - 1; i >= 0; i--)
8181
{
8282
if (tokens[i].StartsWith(".0"))
8383
{
@@ -98,16 +98,9 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
9898
}
9999
else if (token.StartsWith("s", StringComparison.OrdinalIgnoreCase))
100100
{
101-
var value = timeSpan.Seconds;
102-
if (!containsMilliseconds)
103-
{
104-
var roundedMilliseconds = GetRoundedMilliseconds(timeSpan);
105-
106-
if (roundedMilliseconds >= 500)
107-
{
108-
value += 1;
109-
}
110-
}
101+
// If format does not include ms, then include ms in seconds and round before printing
102+
var formatMs = containsMilliseconds ? 0 : timeSpan.Milliseconds / 1000D;
103+
var value = (int)Math.Round(timeSpan.Seconds + formatMs, 0, MidpointRounding.AwayFromZero);
111104
var digits = token.Length;
112105
result.Append(value.ToString("D" + digits));
113106
}
@@ -116,24 +109,24 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
116109
var value = (int)timeSpan.TotalHours;
117110
var digits = token.Length - 2;
118111
result.Append(value.ToString("D" + digits));
119-
timeSpan = TimeSpan.FromHours(timeSpan.TotalHours - value);
112+
timeSpan = new TimeSpan(0, 0, Math.Abs(timeSpan.Minutes), Math.Abs(timeSpan.Seconds), Math.Abs(timeSpan.Milliseconds));
120113
}
121114
else if (token.StartsWith("[m", StringComparison.OrdinalIgnoreCase))
122115
{
123116
var value = (int)timeSpan.TotalMinutes;
124117
var digits = token.Length - 2;
125118
result.Append(value.ToString("D" + digits));
126-
timeSpan = TimeSpan.FromMinutes(timeSpan.TotalMinutes - value);
119+
timeSpan = new TimeSpan(0, 0, 0, Math.Abs(timeSpan.Seconds), Math.Abs(timeSpan.Milliseconds));
127120
}
128121
else if (token.StartsWith("[s", StringComparison.OrdinalIgnoreCase))
129122
{
130123
var value = (int)timeSpan.TotalSeconds;
131124
var digits = token.Length - 2;
132125
result.Append(value.ToString("D" + digits));
133-
timeSpan = TimeSpan.FromSeconds(timeSpan.TotalSeconds - value);
126+
timeSpan = new TimeSpan(0, 0, 0, 0, Math.Abs(timeSpan.Milliseconds));
134127
}
135128
else if (token.StartsWith(".0")) {
136-
var value = GetRoundedMilliseconds(timeSpan);
129+
var value = timeSpan.Milliseconds;
137130
var digits = token.Length - 1;
138131
result.Append("." + value.ToString("D" + digits));
139132
}
@@ -146,17 +139,6 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
146139
return result.ToString();
147140
}
148141

149-
/// <summary>
150-
/// In .Net Core 3.0, TimeSpan is stored with microseconds. As a result, 31:44.500 may be presented as 31:44.499999,
151-
/// and TimeSpan.Microseconds will return 499 instead of 500. This method returns a number of microseconds rounded
152-
/// to a whole.
153-
/// </summary>
154-
private static int GetRoundedMilliseconds(TimeSpan timeSpan)
155-
{
156-
var milliseconds = timeSpan.TotalMilliseconds - (int)timeSpan.TotalSeconds * 1000;
157-
return (int)Math.Round(milliseconds);
158-
}
159-
160142
private static string FormatDate(DateTime date, List<string> tokens, CultureInfo culture)
161143
{
162144
var containsAmPm = ContainsAmPm(tokens);

test/ExcelNumberFormat.Tests/Class1.cs

+5
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ public void TestTimeSpan()
153153
Test(new TimeSpan(1, 2, 31, 45), "[hh]:mm:ss", "26:31:45");
154154
Test(new TimeSpan(1, 2, 31, 44, 500), "[hh]:mm:ss", "26:31:45");
155155
Test(new TimeSpan(1, 2, 31, 44, 500), "[hh]:mm:ss.000", "26:31:44.500");
156+
157+
Test(new TimeSpan(-1, -2, -31, -45), "[hh]:mm:ss", "-26:31:45");
158+
Test(new TimeSpan(0, -2, -31, -45), "[hh]:mm:ss", "-02:31:45");
159+
Test(new TimeSpan(0, -2, -31, -44, -500), "[hh]:mm:ss", "-02:31:45");
160+
Test(new TimeSpan(0, -2, -31, -44, -500), "[hh]:mm:ss.000", "-02:31:44.500");
156161
}
157162

158163
void Test(object value, string format, string expected)

0 commit comments

Comments
 (0)