Skip to content

Instantly share code, notes, and snippets.

@mattjohnsonpint
Created February 9, 2016 05:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mattjohnsonpint/940581e3d93f66d66c5c to your computer and use it in GitHub Desktop.
Save mattjohnsonpint/940581e3d93f66d66c5c to your computer and use it in GitHub Desktop.
Testing ISO8601 formatting performance
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);
}
}
}
@mattjohnsonpint
Copy link
Author

Results:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment