Skip to content

Commit c81eca8

Browse files
lilinusxtqqczze
andauthored
Refactor some DateTime and TimeSpan formatting/parsing methods (#101640)
* Refactor some DateTime and TimeSpan formatting/parsing methods * Fix assertion in TimeSpanParse.Pow10 * Don't use Unsafe in TimeSpanParse.Pow10 * Revert changes to TimeSpanParse.Pow10 * Revert "Revert changes to TimeSpanParse.Pow10" This reverts commit 267d5e8. * Change method name to Pow10UpToMaxFractionDigits * Fix TimeSpanParse.TimeSpanToken.NormalizeAndValidateFraction * Address feedback in TimeSpanParse * Change from Math.Round to uint divison in TimeSpanParse.NormalizeAndValidateFraction * Comment for rounding division in TimeSpanParse.NormalizeAndValidateFraction * Update src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs Co-authored-by: xtqqczze <45661989+xtqqczze@users.noreply.github.com> --------- Co-authored-by: xtqqczze <45661989+xtqqczze@users.noreply.github.com>
1 parent 4585e00 commit c81eca8

File tree

4 files changed

+40
-26
lines changed

4 files changed

+40
-26
lines changed

src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -484,11 +484,11 @@ private static void FormatCustomized<TChar>(
484484
tokenLen = ParseRepeatPattern(format, i, ch);
485485
if (tokenLen <= MaxSecondsFractionDigits)
486486
{
487-
long fraction = (dateTime.Ticks % Calendar.TicksPerSecond);
488-
fraction /= (long)Math.Pow(10, 7 - tokenLen);
487+
int fraction = (int)(dateTime.Ticks % Calendar.TicksPerSecond);
488+
fraction /= TimeSpanParse.Pow10UpToMaxFractionDigits(MaxSecondsFractionDigits - tokenLen);
489489
if (ch == 'f')
490490
{
491-
FormatFraction(ref result, (int)fraction, fixedNumberFormats[tokenLen - 1]);
491+
FormatFraction(ref result, fraction, fixedNumberFormats[tokenLen - 1]);
492492
}
493493
else
494494
{
@@ -507,7 +507,7 @@ private static void FormatCustomized<TChar>(
507507
}
508508
if (effectiveDigits > 0)
509509
{
510-
FormatFraction(ref result, (int)fraction, fixedNumberFormats[effectiveDigits - 1]);
510+
FormatFraction(ref result, fraction, fixedNumberFormats[effectiveDigits - 1]);
511511
}
512512
else
513513
{

src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3179,6 +3179,7 @@ internal static bool ParseDigits(ref __DTString str, int minDigitLen, int maxDig
31793179

31803180
private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, scoped ref double result)
31813181
{
3182+
Debug.Assert(maxDigitLen <= DateTimeFormat.MaxSecondsFractionDigits);
31823183
if (!str.GetNextDigit())
31833184
{
31843185
str.Index--;
@@ -3197,7 +3198,7 @@ private static bool ParseFractionExact(ref __DTString str, int maxDigitLen, scop
31973198
result = result * 10 + str.GetDigit();
31983199
}
31993200

3200-
result /= TimeSpanParse.Pow10(digitLen);
3201+
result /= TimeSpanParse.Pow10UpToMaxFractionDigits(digitLen);
32013202
return digitLen == maxDigitLen;
32023203
}
32033204

src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanFormat.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ private static void FormatCustomized<TChar>(TimeSpan value, scoped ReadOnlySpan<
311311
int seconds = (int)(time / TimeSpan.TicksPerSecond % 60);
312312
int fraction = (int)(time % TimeSpan.TicksPerSecond);
313313

314-
long tmp;
314+
int tmp;
315315
int i = 0;
316316
int tokenLen;
317317

@@ -356,8 +356,8 @@ private static void FormatCustomized<TChar>(TimeSpan value, scoped ReadOnlySpan<
356356
}
357357

358358
tmp = fraction;
359-
tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
360-
DateTimeFormat.FormatFraction(ref result, (int)tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]);
359+
tmp /= TimeSpanParse.Pow10UpToMaxFractionDigits(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
360+
DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1]);
361361
break;
362362
case 'F':
363363
//
@@ -370,7 +370,7 @@ private static void FormatCustomized<TChar>(TimeSpan value, scoped ReadOnlySpan<
370370
}
371371

372372
tmp = fraction;
373-
tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
373+
tmp /= TimeSpanParse.Pow10UpToMaxFractionDigits(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
374374
int effectiveDigits = tokenLen;
375375
while (effectiveDigits > 0)
376376
{
@@ -386,7 +386,7 @@ private static void FormatCustomized<TChar>(TimeSpan value, scoped ReadOnlySpan<
386386
}
387387
if (effectiveDigits > 0)
388388
{
389-
DateTimeFormat.FormatFraction(ref result, (int)tmp, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1]);
389+
DateTimeFormat.FormatFraction(ref result, tmp, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1]);
390390
}
391391
break;
392392
case 'd':

src/libraries/System.Private.CoreLib/src/System/Globalization/TimeSpanParse.cs

+29-16
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
//
4848
////////////////////////////////////////////////////////////////////////////
4949

50+
using System.Buffers.Text;
5051
using System.Diagnostics;
5152
using System.Text;
5253

@@ -114,7 +115,7 @@ public bool NormalizeAndValidateFraction()
114115
if (_zeroes == 0 && _num > MaxFraction)
115116
return false;
116117

117-
int totalDigitsCount = ((int)Math.Floor(Math.Log10(_num))) + 1 + _zeroes;
118+
int totalDigitsCount = FormattingHelpers.CountDigits((uint)_num) + _zeroes;
118119

119120
if (totalDigitsCount == MaxFractionDigits)
120121
{
@@ -132,7 +133,7 @@ public bool NormalizeAndValidateFraction()
132133
// .000001 normalize to 10 ticks
133134
// .1 normalize to 1,000,000 ticks
134135

135-
_num *= (int)Pow10(MaxFractionDigits - totalDigitsCount);
136+
_num *= Pow10UpToMaxFractionDigits(MaxFractionDigits - totalDigitsCount);
136137
return true;
137138
}
138139

@@ -143,7 +144,18 @@ public bool NormalizeAndValidateFraction()
143144
// .099999999 normalize to 1,000,000 ticks
144145

145146
Debug.Assert(_zeroes > 0); // Already validated that in the condition _zeroes == 0 && _num > MaxFraction
146-
_num = (int)Math.Round((double)_num / Pow10(totalDigitsCount - MaxFractionDigits), MidpointRounding.AwayFromZero);
147+
148+
if (_zeroes > MaxFractionDigits)
149+
{
150+
// If there are 8 leading zeroes, it rounds to zero
151+
_num = 0;
152+
return true;
153+
}
154+
155+
Debug.Assert(totalDigitsCount - MaxFractionDigits <= MaxFractionDigits);
156+
uint power = (uint)Pow10UpToMaxFractionDigits(totalDigitsCount - MaxFractionDigits);
157+
// Unsigned integer division, rounding away from zero
158+
_num = (int)(((uint)_num + power / 2) / power);
147159
Debug.Assert(_num < MaxFraction);
148160

149161
return true;
@@ -563,20 +575,21 @@ internal bool SetBadFormatSpecifierFailure(char? formatSpecifierCharacter = null
563575
}
564576
}
565577

566-
internal static long Pow10(int pow)
578+
internal static int Pow10UpToMaxFractionDigits(int pow)
567579
{
568-
return pow switch
569-
{
570-
0 => 1,
571-
1 => 10,
572-
2 => 100,
573-
3 => 1000,
574-
4 => 10000,
575-
5 => 100000,
576-
6 => 1000000,
577-
7 => 10000000,
578-
_ => (long)Math.Pow(10, pow),
579-
};
580+
ReadOnlySpan<int> powersOfTen =
581+
[
582+
1,
583+
10,
584+
100,
585+
1000,
586+
10000,
587+
100000,
588+
1000000,
589+
10000000,
590+
];
591+
Debug.Assert(powersOfTen.Length == MaxFractionDigits + 1);
592+
return powersOfTen[pow];
580593
}
581594

582595
private static bool TryTimeToTicks(bool positive, TimeSpanToken days, TimeSpanToken hours, TimeSpanToken minutes, TimeSpanToken seconds, TimeSpanToken fraction, out long result)

0 commit comments

Comments
 (0)