-
Notifications
You must be signed in to change notification settings - Fork 2.7k
DateTime.ToString(“o”) allocates 32 objects, #7836
Changes from 3 commits
8d69b5d
132e5d1
f49c5d1
262c07a
dfe10ff
bf79a18
1321303
ec5af22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -807,10 +807,6 @@ internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi) | |
case 'M': // Month/Day Date | ||
realFormat = dtfi.MonthDayPattern; | ||
break; | ||
case 'o': | ||
case 'O': | ||
realFormat = RoundtripFormat; | ||
break; | ||
case 'r': | ||
case 'R': // RFC 1123 Standard | ||
realFormat = dtfi.RFC1123Pattern; | ||
|
@@ -848,10 +844,6 @@ internal static String GetRealFormat(String format, DateTimeFormatInfo dtfi) | |
// | ||
private static String ExpandPredefinedFormat(String format, ref DateTime dateTime, ref DateTimeFormatInfo dtfi, ref TimeSpan offset) { | ||
switch (format[0]) { | ||
case 'o': | ||
case 'O': // Round trip format | ||
dtfi = DateTimeFormatInfo.InvariantInfo; | ||
break; | ||
case 'r': | ||
case 'R': // RFC 1123 Standard | ||
if (offset != NullOffset) { | ||
|
@@ -957,12 +949,88 @@ internal static String Format(DateTime dateTime, String format, DateTimeFormatIn | |
} | ||
|
||
if (format.Length == 1) { | ||
switch (format[0]) { | ||
case 'o': | ||
case 'O': | ||
// Fast track for round trip format without going through a format string. | ||
return RoundTripFormat(ref dateTime); | ||
} | ||
|
||
format = ExpandPredefinedFormat(format, ref dateTime, ref dtfi, ref offset); | ||
} | ||
} | ||
|
||
return (FormatCustomized(dateTime, format, dtfi, offset)); | ||
} | ||
|
||
|
||
internal static string RoundTripFormat(ref DateTime dateTime) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
one last thing, you named this method as RoundtripFormat which is same name as the defined string constant. would be better to rename the method to something better (e.g. FormatAsISO8601() or anything you think better).
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good feedback There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I measured it using the Perf tools in Visual Studio. I can show you if you'd like to see it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. right, DateTime is long size so it can passed as value |
||
{ | ||
StringBuilder result = StringBuilderCache.Acquire(); | ||
Append(result, dateTime.Year, 4); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that calling |
||
result.Append('-'); | ||
|
||
Append(result, dateTime.Month, 2); | ||
result.Append('-'); | ||
|
||
Append(result, dateTime.Day, 2); | ||
result.Append('T'); | ||
|
||
Append(result, dateTime.Hour, 2); | ||
result.Append(':'); | ||
|
||
Append(result, dateTime.Minute, 2); | ||
result.Append(':'); | ||
|
||
Append(result, dateTime.Second, 2); | ||
result.Append('.'); | ||
|
||
long fraction = dateTime.Ticks % TimeSpan.TicksPerSecond; | ||
|
||
Append(result, fraction, 7); | ||
|
||
switch (dateTime.Kind) | ||
{ | ||
case DateTimeKind.Local: | ||
{ | ||
TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(dateTime); | ||
|
||
if (offset >= TimeSpan.Zero) | ||
{ | ||
result.Append('+'); | ||
} | ||
else | ||
{ | ||
result.Append('-'); | ||
offset = offset.Negate(); | ||
} | ||
|
||
Append(result, offset.Hours, 2); | ||
result.Append(':'); | ||
Append(result, offset.Minutes, 2); | ||
} | ||
break; | ||
|
||
case DateTimeKind.Utc: | ||
result.Append('Z'); | ||
break; | ||
|
||
default: | ||
break; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: this default case could be removed |
||
} | ||
|
||
return StringBuilderCache.GetStringAndRelease(result); | ||
} | ||
|
||
internal static void Append(StringBuilder builder, long val, int digit) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
nit: could you call this AppendNumber instead. it will make easier to not confuse it with the StringBuilder Append |
||
{ | ||
if (digit > 1) | ||
{ | ||
Append(builder, val / 10, digit - 1); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this may be expensive as it will be recursive call. I would do it in a loop better There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I was torn because its limited in its current use to not being too deep but I can convert to a loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really happy with my options for a loop here. Will still think about it but may stick with recursion if I can't come up with a better idea for how to do it in a loop cheaply. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Every solution I've come up with for the loop requires more allocations and the use of a stack of some kind to append the digits from most significant to least. Given the max recursive depth here is 7 and its usually 2 I think the recursive solution is fine and the code is much more readable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. here is some implementation suggestion but I'll leave it to you to decide if you think the recursion is better here.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The usage of the string builder is expensive. You are first allocating the string builder (even cached), then in the end you are allocating the string each and every time. We have run into perf issues here and we solved them in the following manner: Note that this code does a single allocation for the string, compute the date parts only once. In our tests, it was 15% of the runtime of the default impl. And it can probably be improved still. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am seeing you have removed the second loop. I would suggest we just assert val == 0 if we need this method be reliable in case someone else later use it wrong way |
||
|
||
builder.Append((char)('0' + (val % 10))); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given that EDIT: If not, it's probably worth to open an issue for the JIT team ;) |
||
|
||
|
||
internal static String[] GetAllDateTimes(DateTime dateTime, char format, DateTimeFormatInfo dtfi) | ||
{ | ||
Contract.Requires(dtfi != null); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: using if-statement maybe better than switch here as we are testing 2 values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will do that, it was another thing I debated and switches seemed to be used so much in this file I went with the style of the file :)