Created
February 9, 2016 05:06
-
-
Save mattjohnsonpint/940581e3d93f66d66c5c to your computer and use it in GitHub Desktop.
Testing ISO8601 formatting performance
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Diagnostics; | |
using System.Globalization; | |
namespace DateTimeToIsoStringPerfTests | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
const int n = 1000000; | |
TimeSpan t1 = Test(GetIso8601ToStringO, n); | |
TimeSpan t2 = Test(GetIso8601FormattedDateTime_Old, n); | |
TimeSpan t3 = Test(GetIso8601FormattedDateTime_New, n); | |
Console.WriteLine("Tests ran with {0:N0} iterations", n); | |
Console.WriteLine("GetIso8601ToStringO: {0:F0} ms", t1.TotalMilliseconds); | |
Console.WriteLine("GetIso8601FormattedDateTime_Old: {0:F0} ms", t2.TotalMilliseconds); | |
Console.WriteLine("GetIso8601FormattedDateTime_New: {0:F0} ms", t3.TotalMilliseconds); | |
} | |
private static TimeSpan Test(Func<DateTime, string> method, int iterations) | |
{ | |
string s; | |
var sw = Stopwatch.StartNew(); | |
for (int i = 0; i < iterations; i++) | |
{ | |
long ticks = LongRandom(DateTime.MinValue.Ticks, DateTime.MaxValue.Ticks); | |
DateTime dt = new DateTime(ticks, DateTimeKind.Utc); | |
s = method(dt); | |
} | |
sw.Stop(); | |
return sw.Elapsed; | |
} | |
private static readonly Random Rand = new Random(); | |
private static long LongRandom(long min, long max) | |
{ | |
byte[] buf = new byte[8]; | |
Rand.NextBytes(buf); | |
long longRand = BitConverter.ToInt64(buf, 0); | |
return Math.Abs(longRand % (max - min)) + min; | |
} | |
public static string GetIso8601ToStringO(DateTime dt) | |
{ | |
return dt.ToString("o", CultureInfo.InvariantCulture); | |
} | |
public static string GetIso8601FormattedDateTime_Old(DateTime dt) | |
{ | |
int year = dt.Year; | |
int month = dt.Month; | |
int day = dt.Day; | |
int hour = dt.Hour; | |
int minute = dt.Minute; | |
int second = dt.Second; | |
char[] dateString = new char[28]; | |
int index = 0; | |
// Build the year | |
dateString[index++] = (char)(year / (1000) + 0x30); | |
year = (int)(year % 1000); | |
dateString[index++] = (char)(year / (100) + 0x30); | |
year = (int)(year % 100); | |
dateString[index++] = (char)(year / (10) + 0x30); | |
year = (int)(year % 10); | |
dateString[index++] = (char)(year + 0x30); | |
dateString[index++] = '-'; | |
// Build the month | |
dateString[index++] = (char)(month / (10) + 0x30); | |
month = (int)(month % 10); | |
dateString[index++] = (char)(month + 0x30); | |
dateString[index++] = '-'; | |
// Build the day | |
dateString[index++] = (char)(day / (10) + 0x30); | |
day = (int)(day % 10); | |
dateString[index++] = (char)(day + 0x30); | |
dateString[index++] = 'T'; | |
// Build the hour | |
dateString[index++] = (char)(hour / (10) + 0x30); | |
hour = (int)(hour % 10); | |
dateString[index++] = (char)(hour + 0x30); | |
dateString[index++] = ':'; | |
// Build the minute | |
dateString[index++] = (char)(minute / (10) + 0x30); | |
minute = (int)(minute % 10); | |
dateString[index++] = (char)(minute + 0x30); | |
dateString[index++] = ':'; | |
// Build the second | |
dateString[index++] = (char)(second / (10) + 0x30); | |
second = (int)(second % 10); | |
dateString[index++] = (char)(second + 0x30); | |
dateString[index++] = '.'; | |
// Now calculate the balance ticks | |
DateTime dt2 = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, dt.Minute, dt.Second); | |
long ticks = dt.Ticks - dt2.Ticks; | |
// Build the ticks | |
dateString[index++] = (char)(ticks / (1000000) + 0x30); | |
ticks = (int)(ticks % 1000000); | |
dateString[index++] = (char)(ticks / (100000) + 0x30); | |
ticks = (int)(ticks % 100000); | |
dateString[index++] = (char)(ticks / (10000) + 0x30); | |
ticks = (int)(ticks % 10000); | |
dateString[index++] = (char)(ticks / (1000) + 0x30); | |
ticks = (int)(ticks % 1000); | |
dateString[index++] = (char)(ticks / (100) + 0x30); | |
ticks = (int)(ticks % 100); | |
dateString[index++] = (char)(ticks / (10) + 0x30); | |
ticks = (int)(ticks % 10); | |
dateString[index++] = (char)(ticks + 0x30); | |
dateString[index++] = 'Z'; | |
return new string(dateString); | |
} | |
public static string GetIso8601FormattedDateTime_New(DateTime dt) | |
{ | |
// size the result properly based on kind | |
int l; | |
switch (dt.Kind) | |
{ | |
case DateTimeKind.Utc: | |
l = 28; | |
break; | |
case DateTimeKind.Local: | |
l = 33; | |
break; | |
default: // DateTimeKind.Unspecified: | |
l = 27; | |
break; | |
} | |
char[] dateString = new char[l]; | |
int year = dt.Year; | |
int month = dt.Month; | |
int day = dt.Day; | |
int hour = dt.Hour; | |
int minute = dt.Minute; | |
int second = dt.Second; | |
// Build the year | |
dateString[0] = (char)(year / 1000 % 10 + '0'); | |
dateString[1] = (char)(year / 100 % 10 + '0'); | |
dateString[2] = (char)(year / 10 % 10 + '0'); | |
dateString[3] = (char)(year % 10 + '0'); | |
dateString[4] = '-'; | |
// Build the month | |
dateString[5] = (char)(month / 10 + '0'); | |
dateString[6] = (char)(month % 10 + '0'); | |
dateString[7] = '-'; | |
// Build the day | |
dateString[8] = (char)(day / 10 + '0'); | |
dateString[9] = (char)(day % 10 + '0'); | |
dateString[10] = 'T'; | |
// Build the hour | |
dateString[11] = (char)(hour / 10 + '0'); | |
dateString[12] = (char)(hour % 10 + '0'); | |
dateString[13] = ':'; | |
// Build the minute | |
dateString[14] = (char)(minute / 10 + '0'); | |
dateString[15] = (char)(minute % 10 + '0'); | |
dateString[16] = ':'; | |
// Build the second | |
dateString[17] = (char)(second / 10 + '0'); | |
dateString[18] = (char)(second % 10 + '0'); | |
dateString[19] = '.'; | |
// Now calculate the balance ticks | |
long ticks = dt.Ticks % 10000000; | |
// Build the ticks | |
dateString[20] = (char)(ticks / 1000000 % 10 + '0'); | |
dateString[21] = (char)(ticks / 100000 % 10 + '0'); | |
dateString[22] = (char)(ticks / 10000 % 10 + '0'); | |
dateString[23] = (char)(ticks / 1000 % 10 + '0'); | |
dateString[24] = (char)(ticks / 100 % 10 + '0'); | |
dateString[25] = (char)(ticks / 10 % 10 + '0'); | |
dateString[26] = (char)(ticks % 10 + '0'); | |
switch (dt.Kind) | |
{ | |
case DateTimeKind.Utc: | |
// Apply Z only on UTC kind | |
dateString[27] = 'Z'; | |
break; | |
case DateTimeKind.Local: | |
// Build an offset, such as -08:00 | |
int offset = (int)TimeZoneInfo.Local.GetUtcOffset(dt).TotalMinutes; | |
dateString[27] = offset < 0 ? '-' : '+'; | |
offset = Math.Abs(offset); | |
int h = offset / 60; | |
int m = offset % 60; | |
dateString[28] = (char)(h / 10 + '0'); | |
dateString[29] = (char)(h % 10 + '0'); | |
dateString[30] = ':'; | |
dateString[31] = (char)(m / 10 + '0'); | |
dateString[32] = (char)(m % 10 + '0'); | |
break; | |
} | |
return new string(dateString); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Results:
