Skip to content

Commit ba27ab2

Browse files
Pankratyandersnm
authored andcommitted
Format TimeSpan with respect to milliseconds. Round milliseconds to whole numbers during formatting instead of trimming microseconds (#23)
1 parent bc33180 commit ba27ab2

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

src/ExcelNumberFormat/Formatter.cs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
7676
// NOTE/TODO: assumes there is exactly one [hh], [mm] or [ss] using the integer part of TimeSpan.TotalXXX when formatting.
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();
79+
var containsMilliseconds = false;
80+
for (var i = tokens.Count-1; i >= 0; i--)
81+
{
82+
if (tokens[i].StartsWith(".0"))
83+
{
84+
containsMilliseconds = true;
85+
break;
86+
}
87+
}
88+
7989
for (var i = 0; i < tokens.Count; i++)
8090
{
8191
var token = tokens[i];
@@ -89,6 +99,15 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
8999
else if (token.StartsWith("s", StringComparison.OrdinalIgnoreCase))
90100
{
91101
var value = timeSpan.Seconds;
102+
if (!containsMilliseconds)
103+
{
104+
var roundedMilliseconds = GetRoundedMilliseconds(timeSpan);
105+
106+
if (roundedMilliseconds >= 500)
107+
{
108+
value += 1;
109+
}
110+
}
92111
var digits = token.Length;
93112
result.Append(value.ToString("D" + digits));
94113
}
@@ -114,7 +133,7 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
114133
timeSpan = TimeSpan.FromSeconds(timeSpan.TotalSeconds - value);
115134
}
116135
else if (token.StartsWith(".0")) {
117-
var value = timeSpan.Milliseconds;
136+
var value = GetRoundedMilliseconds(timeSpan);
118137
var digits = token.Length - 1;
119138
result.Append("." + value.ToString("D" + digits));
120139
}
@@ -127,6 +146,17 @@ private static string FormatTimeSpan(TimeSpan timeSpan, List<string> tokens, Cul
127146
return result.ToString();
128147
}
129148

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+
130160
private static string FormatDate(DateTime date, List<string> tokens, CultureInfo culture)
131161
{
132162
var containsAmPm = ContainsAmPm(tokens);

test/ExcelNumberFormat.Tests/Class1.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ public void TestTimeSpan()
149149
Test(TimeSpan.FromHours(100), "[hh]:mm:ss", "100:00:00");
150150
Test(TimeSpan.FromHours(100), "[mm]:ss", "6000:00");
151151
Test(TimeSpan.FromMilliseconds(100 * 60 * 60 * 1000 + 123), "[mm]:ss.000", "6000:00.123");
152+
153+
Test(new TimeSpan(1, 2, 31, 45), "[hh]:mm:ss", "26:31:45");
154+
Test(new TimeSpan(1, 2, 31, 44, 500), "[hh]:mm:ss", "26:31:45");
155+
Test(new TimeSpan(1, 2, 31, 44, 500), "[hh]:mm:ss.000", "26:31:44.500");
152156
}
153157

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

0 commit comments

Comments
 (0)