Last active
October 6, 2016 08:48
-
-
Save aL3891/2644c97e55f2f6c67b1ade15228031b8 to your computer and use it in GitHub Desktop.
dateformat
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 static System.Text.Encoding; | |
using BenchmarkDotNet.Attributes; | |
using BenchmarkDotNet.Configs; | |
using BenchmarkDotNet.Diagnostics.Windows; | |
using BenchmarkDotNet.Running; | |
using Xunit; | |
using System.Globalization; | |
using System.Runtime.CompilerServices; | |
using System.Runtime.InteropServices; | |
using System.Linq; | |
public static class Program | |
{ | |
public unsafe static void Main(string[] args) | |
{ | |
BenchmarkRunner.Run<Benchmark>(); | |
Console.ReadLine(); | |
} | |
public class Config : ManualConfig | |
{ | |
public Config() | |
{ | |
Add(new MemoryDiagnoser()); | |
} | |
} | |
[Config(typeof(Config))] | |
public class Benchmark | |
{ | |
private readonly DateTimeOffset _dateTime = DateTimeOffset.UtcNow; | |
private byte[] buf = new byte[29]; | |
[Benchmark(Baseline = true)] | |
public string ToStringR() | |
{ | |
return _dateTime.ToString("R"); | |
} | |
[Benchmark] | |
public string ToRfc1123StringKristian() | |
{ | |
return DateTimeFormatterKristian.ToRfc1123String(_dateTime); | |
} | |
[Benchmark] | |
public string ToRfc1123StringTore() | |
{ | |
return DateTimeFormatterTore.ToRfc1123String(_dateTime); | |
} | |
[Benchmark] | |
public string ToRfc1123StringAllanv1() | |
{ | |
return DateTimeFormatterAllanV1.ToRfc1123String(_dateTime); | |
} | |
[Benchmark] | |
public string ToRfc1123StringAllanv2() | |
{ | |
return DateTimeFormatterAllanV2.ToRfc1123String(_dateTime); | |
} | |
[Benchmark] | |
public string ToRfc1123StringAllanv3() | |
{ | |
return DateTimeFormatterAllanV3.ToRfc1123String(_dateTime); | |
} | |
[Benchmark] | |
public string ToRfc1123StringAllanv4() | |
{ | |
return DateTimeFormatterAllanV4.ToRfc1123String(_dateTime); | |
} | |
[Benchmark()] | |
public unsafe void ToRfc1123StringAllanv1WithBuffer() | |
{ | |
fixed (byte* b = &buf[0]) | |
DateTimeFormatterAllanV1.ToRfc1123String(_dateTime, b, 0); | |
} | |
[Benchmark] | |
public unsafe void ToRfc1123StringAllanv2WithBuffer() | |
{ | |
fixed (byte* b = &buf[0]) | |
DateTimeFormatterAllanV2.ToRfc1123String(_dateTime, b, 0); | |
} | |
[Benchmark] | |
public unsafe void ToRfc1123StringAllanv3WithBuffer() | |
{ | |
fixed (byte* b = &buf[0]) | |
DateTimeFormatterAllanV3.ToRfc1123String(_dateTime, b, 0); | |
} | |
[Benchmark] | |
public unsafe void ToRfc1123StringAllanv4WithBuffer() | |
{ | |
fixed (byte* b = &buf[0]) | |
DateTimeFormatterAllanV4.ToRfc1123String(_dateTime, b, 0); | |
} | |
} | |
} | |
internal static unsafe class DateTimeFormatterAllanV1 | |
{ | |
private static readonly DateTimeFormatInfo FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; | |
private static readonly byte[] dayNames = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedDayNames)); | |
private static readonly byte[] months = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedMonthGenitiveNames)); | |
private static readonly byte[] years = UTF8.GetBytes(string.Join("", Enumerable.Range(2015, 10).Select(y => y.ToString("0000")))); | |
private static readonly byte[] numbers = UTF8.GetBytes(string.Join("", Enumerable.Range(0, 60).Select(y => y.ToString("00")))); | |
private static readonly byte[] templateBytes = UTF8.GetBytes("ddd, dd MMM yyyy HH:mm:ss GMT"); | |
public static unsafe string ToRfc1123String(this DateTimeOffset dateTime) | |
{ | |
byte* buf = stackalloc byte[29]; | |
ToRfc1123String(dateTime, buf, 0); | |
return UTF8.GetString(buf, 29); | |
} | |
public static unsafe void ToRfc1123String(this DateTimeOffset dto, byte* target, int offset) | |
{ | |
var tmpIndex = 0; | |
var dateTime = dto.UtcDateTime; | |
fixed (byte* t = &templateBytes[0]) | |
Unsafe.CopyBlock(target, t, (uint)templateBytes.Length); | |
switch (dateTime.DayOfWeek) | |
{ | |
case DayOfWeek.Sunday: target[0] = dayNames[(0 * 3) + 0]; target[1] = dayNames[(0 * 3) + 1]; target[2] = dayNames[(0 * 3) + 2]; break; | |
case DayOfWeek.Monday: target[0] = dayNames[(1 * 3) + 0]; target[1] = dayNames[(1 * 3) + 1]; target[2] = dayNames[(1 * 3) + 2]; break; | |
case DayOfWeek.Tuesday: target[0] = dayNames[(2 * 3) + 0]; target[1] = dayNames[(2 * 3) + 1]; target[2] = dayNames[(2 * 3) + 2]; break; | |
case DayOfWeek.Wednesday: target[0] = dayNames[(3 * 3) + 0]; target[1] = dayNames[(3 * 3) + 1]; target[2] = dayNames[(3 * 3) + 2]; break; | |
case DayOfWeek.Thursday: target[0] = dayNames[(4 * 3) + 0]; target[1] = dayNames[(4 * 3) + 1]; target[2] = dayNames[(4 * 3) + 2]; break; | |
case DayOfWeek.Friday: target[0] = dayNames[(5 * 3) + 0]; target[1] = dayNames[(5 * 3) + 1]; target[2] = dayNames[(5 * 3) + 2]; break; | |
case DayOfWeek.Saturday: target[0] = dayNames[(6 * 3) + 0]; target[1] = dayNames[(6 * 3) + 1]; target[2] = dayNames[(6 * 3) + 2]; break; | |
} | |
tmpIndex = dateTime.Day * 2; | |
target[5] = numbers[tmpIndex]; | |
target[6] = numbers[tmpIndex + 1]; | |
switch (dateTime.Month) | |
{ | |
case 1: target[8] = months[(0 * 3) + 0]; target[9] = months[(0 * 3) + 1]; target[10] = months[(0 * 3) + 2]; break; | |
case 2: target[8] = months[(1 * 3) + 0]; target[9] = months[(1 * 3) + 1]; target[10] = months[(1 * 3) + 2]; break; | |
case 3: target[8] = months[(2 * 3) + 0]; target[9] = months[(2 * 3) + 1]; target[10] = months[(2 * 3) + 2]; break; | |
case 4: target[8] = months[(3 * 3) + 0]; target[9] = months[(3 * 3) + 1]; target[10] = months[(3 * 3) + 2]; break; | |
case 5: target[8] = months[(4 * 3) + 0]; target[9] = months[(4 * 3) + 1]; target[10] = months[(4 * 3) + 2]; break; | |
case 6: target[8] = months[(5 * 3) + 0]; target[9] = months[(5 * 3) + 1]; target[10] = months[(5 * 3) + 2]; break; | |
case 7: target[8] = months[(6 * 3) + 0]; target[9] = months[(6 * 3) + 1]; target[10] = months[(6 * 3) + 2]; break; | |
case 8: target[8] = months[(7 * 3) + 0]; target[9] = months[(7 * 3) + 1]; target[10] = months[(7 * 3) + 2]; break; | |
case 9: target[8] = months[(8 * 3) + 0]; target[9] = months[(8 * 3) + 1]; target[10] = months[(8 * 3) + 2]; break; | |
case 10: target[8] = months[(9 * 3) + 0]; target[9] = months[(9 * 3) + 1]; target[10] = months[(9 * 3) + 2]; break; | |
case 11: target[8] = months[(10 * 3) + 0]; target[9] = months[(10 * 3) + 1]; target[10] = months[(10 * 3) + 2]; break; | |
case 12: target[8] = months[(11 * 3) + 0]; target[9] = months[(11 * 3) + 1]; target[10] = months[(11 * 3) + 2]; break; | |
} | |
tmpIndex = (dateTime.Year - 2015) * 4; | |
target[12] = years[tmpIndex]; | |
target[13] = years[tmpIndex + 1]; | |
target[14] = years[tmpIndex + 2]; | |
target[15] = years[tmpIndex + 3]; | |
tmpIndex = dateTime.Hour * 2; | |
target[17] = numbers[tmpIndex]; | |
target[18] = numbers[tmpIndex + 1]; | |
tmpIndex = dateTime.Minute * 2; | |
target[20] = numbers[tmpIndex]; | |
target[21] = numbers[tmpIndex + 1]; | |
tmpIndex = dateTime.Second * 2; | |
target[23] = numbers[tmpIndex]; | |
target[24] = numbers[tmpIndex + 1]; | |
} | |
} | |
internal static class DateTimeFormatterAllanV2 | |
{ | |
private static unsafe byte* dayNames; | |
private static unsafe byte* months; | |
private static unsafe byte* numbers; | |
private static unsafe byte* template; | |
private static unsafe byte* years; | |
static unsafe DateTimeFormatterAllanV2() | |
{ | |
var FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; | |
var dayNameBytes = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedDayNames)); | |
dayNames = (byte*)Marshal.AllocHGlobal(dayNameBytes.Length).ToPointer(); | |
fixed (byte* dn = &dayNameBytes[0]) | |
Buffer.MemoryCopy(dn, dayNames, dayNameBytes.Length, dayNameBytes.Length); | |
var monthsBytes = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedMonthGenitiveNames)); | |
months = (byte*)Marshal.AllocHGlobal(monthsBytes.Length).ToPointer(); | |
fixed (byte* m = &monthsBytes[0]) | |
Buffer.MemoryCopy(m, months, monthsBytes.Length, monthsBytes.Length); | |
var yearsBytes = UTF8.GetBytes(string.Join("", Enumerable.Range(2015, 10).Select(y => y.ToString("0000")))); | |
years = (byte*)Marshal.AllocHGlobal(yearsBytes.Length).ToPointer(); | |
fixed (byte* y = &yearsBytes[0]) | |
Buffer.MemoryCopy(y, years, yearsBytes.Length, yearsBytes.Length); | |
var numbersBytes = UTF8.GetBytes(string.Join("", Enumerable.Range(0, 60).Select(y => y.ToString("00")))); | |
numbers = (byte*)Marshal.AllocHGlobal(numbersBytes.Length).ToPointer(); | |
fixed (byte* n = &numbersBytes[0]) | |
Buffer.MemoryCopy(n, numbers, numbersBytes.Length, numbersBytes.Length); | |
var templateBytes = UTF8.GetBytes("ddd, dd MMM yyyy HH:mm:ss GMT"); | |
template = (byte*)Marshal.AllocHGlobal(templateBytes.Length).ToPointer(); | |
fixed (byte* t = &templateBytes[0]) | |
Buffer.MemoryCopy(t, template, templateBytes.Length, templateBytes.Length); | |
} | |
public static unsafe string ToRfc1123String(DateTimeOffset dateTime) | |
{ | |
byte* buf = stackalloc byte[29]; | |
ToRfc1123String(dateTime, buf, 0); | |
return UTF8.GetString(buf, 29); | |
} | |
public static unsafe void ToRfc1123String(DateTimeOffset dto, byte* target, int offset) | |
{ | |
var tmpIndex = 0; | |
var dateTime = dto.UtcDateTime; | |
Unsafe.CopyBlock(target, template, 29); | |
switch (dateTime.DayOfWeek) | |
{ | |
case DayOfWeek.Sunday: target[0] = dayNames[(0 * 3) + 0]; target[1] = dayNames[(0 * 3) + 1]; target[2] = dayNames[(0 * 3) + 2]; break; | |
case DayOfWeek.Monday: target[0] = dayNames[(1 * 3) + 0]; target[1] = dayNames[(1 * 3) + 1]; target[2] = dayNames[(1 * 3) + 2]; break; | |
case DayOfWeek.Tuesday: target[0] = dayNames[(2 * 3) + 0]; target[1] = dayNames[(2 * 3) + 1]; target[2] = dayNames[(2 * 3) + 2]; break; | |
case DayOfWeek.Wednesday: target[0] = dayNames[(3 * 3) + 0]; target[1] = dayNames[(3 * 3) + 1]; target[2] = dayNames[(3 * 3) + 2]; break; | |
case DayOfWeek.Thursday: target[0] = dayNames[(4 * 3) + 0]; target[1] = dayNames[(4 * 3) + 1]; target[2] = dayNames[(4 * 3) + 2]; break; | |
case DayOfWeek.Friday: target[0] = dayNames[(5 * 3) + 0]; target[1] = dayNames[(5 * 3) + 1]; target[2] = dayNames[(5 * 3) + 2]; break; | |
case DayOfWeek.Saturday: target[0] = dayNames[(6 * 3) + 0]; target[1] = dayNames[(6 * 3) + 1]; target[2] = dayNames[(6 * 3) + 2]; break; | |
} | |
tmpIndex = dateTime.Day * 2; | |
target[5] = numbers[tmpIndex]; | |
target[6] = numbers[tmpIndex + 1]; | |
switch (dateTime.Month) | |
{ | |
case 1: target[8] = months[(0 * 3) + 0]; target[9] = months[(0 * 3) + 1]; target[10] = months[(0 * 3) + 2]; break; | |
case 2: target[8] = months[(1 * 3) + 0]; target[9] = months[(1 * 3) + 1]; target[10] = months[(1 * 3) + 2]; break; | |
case 3: target[8] = months[(2 * 3) + 0]; target[9] = months[(2 * 3) + 1]; target[10] = months[(2 * 3) + 2]; break; | |
case 4: target[8] = months[(3 * 3) + 0]; target[9] = months[(3 * 3) + 1]; target[10] = months[(3 * 3) + 2]; break; | |
case 5: target[8] = months[(4 * 3) + 0]; target[9] = months[(4 * 3) + 1]; target[10] = months[(4 * 3) + 2]; break; | |
case 6: target[8] = months[(5 * 3) + 0]; target[9] = months[(5 * 3) + 1]; target[10] = months[(5 * 3) + 2]; break; | |
case 7: target[8] = months[(6 * 3) + 0]; target[9] = months[(6 * 3) + 1]; target[10] = months[(6 * 3) + 2]; break; | |
case 8: target[8] = months[(7 * 3) + 0]; target[9] = months[(7 * 3) + 1]; target[10] = months[(7 * 3) + 2]; break; | |
case 9: target[8] = months[(8 * 3) + 0]; target[9] = months[(8 * 3) + 1]; target[10] = months[(8 * 3) + 2]; break; | |
case 10: target[8] = months[(9 * 3) + 0]; target[9] = months[(9 * 3) + 1]; target[10] = months[(9 * 3) + 2]; break; | |
case 11: target[8] = months[(10 * 3) + 0]; target[9] = months[(10 * 3) + 1]; target[10] = months[(10 * 3) + 2]; break; | |
case 12: target[8] = months[(11 * 3) + 0]; target[9] = months[(11 * 3) + 1]; target[10] = months[(11 * 3) + 2]; break; | |
} | |
tmpIndex = (dateTime.Year - 2015) * 4; | |
target[12] = years[tmpIndex]; | |
target[13] = years[tmpIndex + 1]; | |
target[14] = years[tmpIndex + 2]; | |
target[15] = years[tmpIndex + 3]; | |
tmpIndex = dateTime.Hour * 2; | |
target[17] = numbers[tmpIndex]; | |
target[18] = numbers[tmpIndex + 1]; | |
tmpIndex = dateTime.Minute * 2; | |
target[20] = numbers[tmpIndex]; | |
target[21] = numbers[tmpIndex + 1]; | |
tmpIndex = dateTime.Second * 2; | |
target[23] = numbers[tmpIndex]; | |
target[24] = numbers[tmpIndex + 1]; | |
} | |
} | |
internal static class DateTimeFormatterAllanV3 | |
{ | |
private const long TicksPerSecond = 10000000L; | |
private const long TicksPerDay = 864000000000L; | |
private const int DaysPerYear = 365; | |
private const int DaysPer4Years = 1461; | |
private const int DaysPer100Years = 36524; | |
private const int DaysPer400Years = 146097; | |
private static readonly int[] DaysToMonth365 = new int[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; | |
private static readonly int[] DaysToMonth366 = new int[] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; | |
private static unsafe byte* dayNames; | |
private static unsafe byte* months; | |
private static unsafe byte* numbers; | |
private static unsafe byte* template; | |
private static unsafe byte* years; | |
static unsafe DateTimeFormatterAllanV3() | |
{ | |
var FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; | |
var dayNameBytes = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedDayNames)); | |
dayNames = (byte*)Marshal.AllocHGlobal(dayNameBytes.Length).ToPointer(); | |
fixed (byte* dn = &dayNameBytes[0]) | |
Buffer.MemoryCopy(dn, dayNames, dayNameBytes.Length, dayNameBytes.Length); | |
var monthsBytes = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedMonthGenitiveNames)); | |
months = (byte*)Marshal.AllocHGlobal(monthsBytes.Length).ToPointer(); | |
fixed (byte* m = &monthsBytes[0]) | |
Buffer.MemoryCopy(m, months, monthsBytes.Length, monthsBytes.Length); | |
var yearsBytes = UTF8.GetBytes(string.Join("", Enumerable.Range(2015, 10).Select(y => y.ToString("0000")))); | |
years = (byte*)Marshal.AllocHGlobal(yearsBytes.Length).ToPointer(); | |
fixed (byte* y = &yearsBytes[0]) | |
Buffer.MemoryCopy(y, years, yearsBytes.Length, yearsBytes.Length); | |
var numbersBytes = UTF8.GetBytes(string.Join("", Enumerable.Range(0, 60).Select(y => y.ToString("00")))); | |
numbers = (byte*)Marshal.AllocHGlobal(numbersBytes.Length).ToPointer(); | |
fixed (byte* n = &numbersBytes[0]) | |
Buffer.MemoryCopy(n, numbers, numbersBytes.Length, numbersBytes.Length); | |
var templateBytes = UTF8.GetBytes("ddd, dd MMM yyyy HH:mm:ss GMT"); | |
template = (byte*)Marshal.AllocHGlobal(templateBytes.Length).ToPointer(); | |
fixed (byte* t = &templateBytes[0]) | |
Buffer.MemoryCopy(t, template, templateBytes.Length, templateBytes.Length); | |
} | |
public static unsafe string ToRfc1123String(DateTimeOffset dateTime) | |
{ | |
byte* buf = stackalloc byte[29]; | |
ToRfc1123String(dateTime, buf, 0); | |
return UTF8.GetString(buf, 29); | |
} | |
public static unsafe void ToRfc1123String(DateTimeOffset dto, byte* target, int offset) | |
{ | |
var tmpIndex = 0; | |
var dateTime = dto.UtcDateTime; | |
Unsafe.CopyBlock(target, template, 29); | |
//https://github.com/dotnet/coreclr/blob/61cb42ceb8f6f542606c7863c7e26edea7b9653c/src/mscorlib/src/System/DateTime.cs#L742 | |
Int64 ticks = dateTime.Ticks; | |
// n = number of days since 1/1/0001 | |
int n = (int)(ticks / TicksPerDay); | |
// y400 = number of whole 400-year periods since 1/1/0001 | |
int y400 = n / DaysPer400Years; | |
// n = day number within 400-year period | |
n -= y400 * DaysPer400Years; | |
// y100 = number of whole 100-year periods within 400-year period | |
int y100 = n / DaysPer100Years; | |
// Last 100-year period has an extra day, so decrement result if 4 | |
if (y100 == 4) y100 = 3; | |
// n = day number within 100-year period | |
n -= y100 * DaysPer100Years; | |
// y4 = number of whole 4-year periods within 100-year period | |
int y4 = n / DaysPer4Years; | |
// n = day number within 4-year period | |
n -= y4 * DaysPer4Years; | |
// y1 = number of whole years within 4-year period | |
int y1 = n / DaysPerYear; | |
// Last year has an extra day, so decrement result if 4 | |
if (y1 == 4) y1 = 3; | |
// If year was requested, compute and return it | |
var year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; | |
// n = day number within year | |
n -= y1 * DaysPerYear; | |
// If day-of-year was requested, return it | |
// Leap year calculation looks different from IsLeapYear since y1, y4, | |
// and y100 are relative to year 1, not year 0 | |
bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); | |
int[] days = leapYear ? DaysToMonth366 : DaysToMonth365; | |
// All months have less than 32 days, so n >> 5 is a good conservative | |
// estimate for the month | |
int month = n >> 5 + 1; | |
// m = 1-based month number | |
while (n >= days[month]) | |
month++; | |
// If month was requested, return it | |
// Return 1-based day-of-month | |
var day = n - days[month - 1] + 1; | |
switch (dateTime.DayOfWeek) | |
{ | |
case DayOfWeek.Sunday: target[0] = dayNames[(0 * 3) + 0]; target[1] = dayNames[(0 * 3) + 1]; target[2] = dayNames[(0 * 3) + 2]; break; | |
case DayOfWeek.Monday: target[0] = dayNames[(1 * 3) + 0]; target[1] = dayNames[(1 * 3) + 1]; target[2] = dayNames[(1 * 3) + 2]; break; | |
case DayOfWeek.Tuesday: target[0] = dayNames[(2 * 3) + 0]; target[1] = dayNames[(2 * 3) + 1]; target[2] = dayNames[(2 * 3) + 2]; break; | |
case DayOfWeek.Wednesday: target[0] = dayNames[(3 * 3) + 0]; target[1] = dayNames[(3 * 3) + 1]; target[2] = dayNames[(3 * 3) + 2]; break; | |
case DayOfWeek.Thursday: target[0] = dayNames[(4 * 3) + 0]; target[1] = dayNames[(4 * 3) + 1]; target[2] = dayNames[(4 * 3) + 2]; break; | |
case DayOfWeek.Friday: target[0] = dayNames[(5 * 3) + 0]; target[1] = dayNames[(5 * 3) + 1]; target[2] = dayNames[(5 * 3) + 2]; break; | |
case DayOfWeek.Saturday: target[0] = dayNames[(6 * 3) + 0]; target[1] = dayNames[(6 * 3) + 1]; target[2] = dayNames[(6 * 3) + 2]; break; | |
} | |
tmpIndex = day * 2; | |
target[5] = numbers[tmpIndex]; | |
target[6] = numbers[tmpIndex + 1]; | |
switch (month) | |
{ | |
case 1: target[8] = months[(0 * 3) + 0]; target[9] = months[(0 * 3) + 1]; target[10] = months[(0 * 3) + 2]; break; | |
case 2: target[8] = months[(1 * 3) + 0]; target[9] = months[(1 * 3) + 1]; target[10] = months[(1 * 3) + 2]; break; | |
case 3: target[8] = months[(2 * 3) + 0]; target[9] = months[(2 * 3) + 1]; target[10] = months[(2 * 3) + 2]; break; | |
case 4: target[8] = months[(3 * 3) + 0]; target[9] = months[(3 * 3) + 1]; target[10] = months[(3 * 3) + 2]; break; | |
case 5: target[8] = months[(4 * 3) + 0]; target[9] = months[(4 * 3) + 1]; target[10] = months[(4 * 3) + 2]; break; | |
case 6: target[8] = months[(5 * 3) + 0]; target[9] = months[(5 * 3) + 1]; target[10] = months[(5 * 3) + 2]; break; | |
case 7: target[8] = months[(6 * 3) + 0]; target[9] = months[(6 * 3) + 1]; target[10] = months[(6 * 3) + 2]; break; | |
case 8: target[8] = months[(7 * 3) + 0]; target[9] = months[(7 * 3) + 1]; target[10] = months[(7 * 3) + 2]; break; | |
case 9: target[8] = months[(8 * 3) + 0]; target[9] = months[(8 * 3) + 1]; target[10] = months[(8 * 3) + 2]; break; | |
case 10: target[8] = months[(9 * 3) + 0]; target[9] = months[(9 * 3) + 1]; target[10] = months[(9 * 3) + 2]; break; | |
case 11: target[8] = months[(10 * 3) + 0]; target[9] = months[(10 * 3) + 1]; target[10] = months[(10 * 3) + 2]; break; | |
case 12: target[8] = months[(11 * 3) + 0]; target[9] = months[(11 * 3) + 1]; target[10] = months[(11 * 3) + 2]; break; | |
} | |
tmpIndex = (year - 2015) * 4; | |
target[12] = years[tmpIndex]; | |
target[13] = years[tmpIndex + 1]; | |
target[14] = years[tmpIndex + 2]; | |
target[15] = years[tmpIndex + 3]; | |
tmpIndex = dateTime.Hour * 2; | |
target[17] = numbers[tmpIndex]; | |
target[18] = numbers[tmpIndex + 1]; | |
tmpIndex = dateTime.Minute * 2; | |
target[20] = numbers[tmpIndex]; | |
target[21] = numbers[tmpIndex + 1]; | |
tmpIndex = dateTime.Second * 2; | |
target[23] = numbers[tmpIndex]; | |
target[24] = numbers[tmpIndex + 1]; | |
} | |
} | |
internal static class DateTimeFormatterAllanV4 | |
{ | |
private const long TicksPerSecond = 10000000L; | |
private const long TicksPerDay = 864000000000L; | |
private const int DaysPerYear = 365; | |
private const int DaysPer4Years = 1461; | |
private const int DaysPer100Years = 36524; | |
private const int DaysPer400Years = 146097; | |
private static readonly int[] DaysToMonth365 = new int[] { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; | |
private static readonly int[] DaysToMonth366 = new int[] { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }; | |
private static volatile unsafe byte* dayNames; | |
private static volatile unsafe byte* months; | |
private static volatile unsafe short* numbers; | |
private static volatile unsafe byte* template; | |
private static volatile unsafe int* years; | |
static unsafe DateTimeFormatterAllanV4() | |
{ | |
var FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; | |
var dayNameBytes = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedDayNames)); | |
dayNames = (byte*)Marshal.AllocHGlobal(dayNameBytes.Length).ToPointer(); | |
fixed (byte* dn = &dayNameBytes[0]) | |
Unsafe.CopyBlock(dayNames, dn, (uint)dayNameBytes.Length); | |
var monthsBytes = UTF8.GetBytes(string.Join("", FormatInfo.AbbreviatedMonthGenitiveNames)); | |
months = (byte*)Marshal.AllocHGlobal(monthsBytes.Length).ToPointer(); | |
fixed (byte* m = &monthsBytes[0]) | |
Unsafe.CopyBlock(months, m, (uint)monthsBytes.Length); | |
var yearsBytes = UTF8.GetBytes(string.Join("", Enumerable.Range(2000, 50).Select(y => y.ToString("0000")))); | |
years = (int*)Marshal.AllocHGlobal(yearsBytes.Length).ToPointer(); | |
fixed (byte* y = &yearsBytes[0]) | |
Unsafe.CopyBlock(years, y, (uint)yearsBytes.Length); | |
var numbersBytes = UTF8.GetBytes(string.Join("", Enumerable.Range(0, 60).Select(y => y.ToString("00")))); | |
numbers = (short*)Marshal.AllocHGlobal(numbersBytes.Length).ToPointer(); | |
fixed (byte* n = &numbersBytes[0]) | |
Unsafe.CopyBlock(numbers, n, (uint)numbersBytes.Length); | |
var templateBytes = UTF8.GetBytes("ddd, dd MMM yyyy HH:mm:ss GMT"); | |
template = (byte*)Marshal.AllocHGlobal(templateBytes.Length).ToPointer(); | |
fixed (byte* t = &templateBytes[0]) | |
Unsafe.CopyBlock(template, t, (uint)templateBytes.Length); | |
} | |
public static unsafe string ToRfc1123String(DateTimeOffset dateTime) | |
{ | |
byte* buf = stackalloc byte[29]; | |
ToRfc1123String(dateTime, buf, 0); | |
return UTF8.GetString(buf, 29); | |
} | |
public static unsafe void ToRfc1123String(DateTimeOffset dto, byte* target, int offset) | |
{ | |
var dateTime = dto.UtcDateTime; | |
((decimal*)target)[0] = ((decimal*)template)[0]; | |
((long*)target)[2] = ((long*)template)[2]; | |
((int*)target)[6] = ((int*)template)[6]; | |
target[28] = template[28]; | |
short* targetAsShort = (short*)target; | |
//https://github.com/dotnet/coreclr/blob/61cb42ceb8f6f542606c7863c7e26edea7b9653c/src/mscorlib/src/System/DateTime.cs#L742 | |
long ticks = dateTime.Ticks; | |
// n = number of days since 1/1/0001 | |
int n = (int)(ticks / TicksPerDay); | |
// y400 = number of whole 400-year periods since 1/1/0001 | |
int y400 = n / DaysPer400Years; | |
// n = day number within 400-year period | |
n -= y400 * DaysPer400Years; | |
// y100 = number of whole 100-year periods within 400-year period | |
int y100 = n / DaysPer100Years; | |
// Last 100-year period has an extra day, so decrement result if 4 | |
if (y100 == 4) y100 = 3; | |
// n = day number within 100-year period | |
n -= y100 * DaysPer100Years; | |
// y4 = number of whole 4-year periods within 100-year period | |
int y4 = n / DaysPer4Years; | |
// n = day number within 4-year period | |
n -= y4 * DaysPer4Years; | |
// y1 = number of whole years within 4-year period | |
int y1 = n / DaysPerYear; | |
// Last year has an extra day, so decrement result if 4 | |
if (y1 == 4) y1 = 3; | |
// If year was requested, compute and return it | |
var year = y400 * 400 + y100 * 100 + y4 * 4 + y1 + 1; | |
// n = day number within year | |
n -= y1 * DaysPerYear; | |
// If day-of-year was requested, return it | |
// Leap year calculation looks different from IsLeapYear since y1, y4, | |
// and y100 are relative to year 1, not year 0 | |
bool leapYear = y1 == 3 && (y4 != 24 || y100 == 3); | |
int[] days = leapYear ? DaysToMonth366 : DaysToMonth365; | |
// All months have less than 32 days, so n >> 5 is a good conservative | |
// estimate for the month | |
int month = n >> 5 + 1; | |
// m = 1-based month number | |
while (n >= days[month]) | |
month++; | |
// If month was requested, return it | |
// Return 1-based day-of-month | |
var day = n - days[month - 1] + 1; | |
switch (dateTime.DayOfWeek) | |
{ | |
case DayOfWeek.Sunday: targetAsShort[0] = *(short*)(dayNames + (0 * 3) + 0); target[2] = dayNames[(0 * 3) + 2]; break; | |
case DayOfWeek.Monday: targetAsShort[0] = *(short*)(dayNames + (1 * 3) + 0); target[2] = dayNames[(1 * 3) + 2]; break; | |
case DayOfWeek.Tuesday: targetAsShort[0] = *(short*)(dayNames + (2 * 3) + 0); target[2] = dayNames[(2 * 3) + 2]; break; | |
case DayOfWeek.Wednesday: targetAsShort[0] = *(short*)(dayNames + (3 * 3) + 0); target[2] = dayNames[(3 * 3) + 2]; break; | |
case DayOfWeek.Thursday: targetAsShort[0] = *(short*)(dayNames + (4 * 3) + 0); target[2] = dayNames[(4 * 3) + 2]; break; | |
case DayOfWeek.Friday: targetAsShort[0] = *(short*)(dayNames + (5 * 3) + 0); target[2] = dayNames[(5 * 3) + 2]; break; | |
case DayOfWeek.Saturday: targetAsShort[0] = *(short*)(dayNames + (6 * 3) + 0); target[2] = dayNames[(6 * 3) + 2]; break; | |
} | |
*(short*)(target + 5) = numbers[day]; | |
switch (month) | |
{ | |
case 1: targetAsShort[4] = *(short*)(months + (0 * 3) + 0); target[10] = months[(0 * 3) + 2]; break; | |
case 2: targetAsShort[4] = *(short*)(months + (1 * 3) + 0); target[10] = months[(1 * 3) + 2]; break; | |
case 3: targetAsShort[4] = *(short*)(months + (2 * 3) + 0); target[10] = months[(2 * 3) + 2]; break; | |
case 4: targetAsShort[4] = *(short*)(months + (3 * 3) + 0); target[10] = months[(3 * 3) + 2]; break; | |
case 5: targetAsShort[4] = *(short*)(months + (4 * 3) + 0); target[10] = months[(4 * 3) + 2]; break; | |
case 6: targetAsShort[4] = *(short*)(months + (5 * 3) + 0); target[10] = months[(5 * 3) + 2]; break; | |
case 7: targetAsShort[4] = *(short*)(months + (6 * 3) + 0); target[10] = months[(6 * 3) + 2]; break; | |
case 8: targetAsShort[4] = *(short*)(months + (7 * 3) + 0); target[10] = months[(7 * 3) + 2]; break; | |
case 9: targetAsShort[4] = *(short*)(months + (8 * 3) + 0); target[10] = months[(8 * 3) + 2]; break; | |
case 10: targetAsShort[4] = *(short*)(months + (9 * 3) + 0); target[10] = months[(9 * 3) + 2]; break; | |
case 11: targetAsShort[4] = *(short*)(months + (10 * 3) + 0); target[10] = months[(10 * 3) + 2]; break; | |
case 12: targetAsShort[4] = *(short*)(months + (11 * 3) + 0); target[10] = months[(11 * 3) + 2]; break; | |
} | |
if (year < 2050 && year > 2000) | |
((int*)target)[3] = years[(year - 2000)]; | |
else | |
{ | |
var bytes = UTF8.GetBytes(year.ToString("0000")); | |
target[12] = bytes[0]; | |
target[13] = bytes[1]; | |
target[14] = bytes[2]; | |
target[15] = bytes[3]; | |
} | |
*(short*)(target + 17) = numbers[dateTime.Hour]; | |
*(short*)(target + 20) = numbers[dateTime.Minute]; | |
*(short*)(target + 23) = numbers[dateTime.Second]; | |
} | |
} | |
internal static class DateTimeFormatterKristian | |
{ | |
private static readonly DateTimeFormatInfo FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; | |
private static readonly byte[] MonBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Monday)); | |
private static readonly byte[] TueBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Tuesday)); | |
private static readonly byte[] WedBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Wednesday)); | |
private static readonly byte[] ThuBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Thursday)); | |
private static readonly byte[] FriBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Friday)); | |
private static readonly byte[] SatBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Saturday)); | |
private static readonly byte[] SunBytes = UTF8.GetBytes(GetDayName(DayOfWeek.Sunday)); | |
private static readonly byte[] JanBytes = UTF8.GetBytes(GetMonthName(1)); | |
private static readonly byte[] FebBytes = UTF8.GetBytes(GetMonthName(2)); | |
private static readonly byte[] MarBytes = UTF8.GetBytes(GetMonthName(3)); | |
private static readonly byte[] AprBytes = UTF8.GetBytes(GetMonthName(4)); | |
private static readonly byte[] MayBytes = UTF8.GetBytes(GetMonthName(5)); | |
private static readonly byte[] JunBytes = UTF8.GetBytes(GetMonthName(6)); | |
private static readonly byte[] JulBytes = UTF8.GetBytes(GetMonthName(7)); | |
private static readonly byte[] AugBytes = UTF8.GetBytes(GetMonthName(8)); | |
private static readonly byte[] SepBytes = UTF8.GetBytes(GetMonthName(9)); | |
private static readonly byte[] OctBytes = UTF8.GetBytes(GetMonthName(10)); | |
private static readonly byte[] NovBytes = UTF8.GetBytes(GetMonthName(11)); | |
private static readonly byte[] DecBytes = UTF8.GetBytes(GetMonthName(12)); | |
private static readonly byte[] GmtBytes = UTF8.GetBytes("GMT"); | |
// The format is "ddd, dd MMM yyyy HH:mm:ss GMT". | |
private const int Rfc1123DateLength = 29; | |
// ASCII numbers are in the range 48 - 57. | |
private const int AsciiNumberOffset = 0x30; | |
private const char Comma = ','; | |
private const char Space = ' '; | |
private const char Colon = ':'; | |
public static unsafe string ToRfc1123String(this DateTimeOffset dateTime) | |
{ | |
var offset = 0; | |
char* target = stackalloc char[Rfc1123DateLength]; | |
var universalDateTime = dateTime.ToUniversalTime(); | |
offset = FormatDayOfWeek(universalDateTime.DayOfWeek, target, offset); | |
offset = Format(Comma, target, offset); | |
offset = Format(Space, target, offset); | |
offset = FormatNumber(universalDateTime.Day, target, offset); | |
offset = Format(Space, target, offset); | |
offset = FormatMonth(universalDateTime.Month, target, offset); | |
offset = Format(Space, target, offset); | |
offset = FormatYear(universalDateTime.Year, target, offset); | |
offset = Format(Space, target, offset); | |
offset = FormatTimeOfDay(universalDateTime.TimeOfDay, target, offset); | |
offset = Format(Space, target, offset); | |
offset = Format(GmtBytes, target, offset); | |
return new string(target, 0, offset); | |
} | |
private static unsafe int FormatDayOfWeek(DayOfWeek dayOfWeek, char* target, int offset) | |
{ | |
switch (dayOfWeek) | |
{ | |
case DayOfWeek.Sunday: return Format(SunBytes, target, offset); | |
case DayOfWeek.Monday: return Format(MonBytes, target, offset); | |
case DayOfWeek.Tuesday: return Format(TueBytes, target, offset); | |
case DayOfWeek.Wednesday: return Format(WedBytes, target, offset); | |
case DayOfWeek.Thursday: return Format(ThuBytes, target, offset); | |
case DayOfWeek.Friday: return Format(FriBytes, target, offset); | |
case DayOfWeek.Saturday: return Format(SatBytes, target, offset); | |
default: return offset; | |
} | |
} | |
private static unsafe int FormatMonth(int month, char* target, int offset) | |
{ | |
switch (month) | |
{ | |
case 1: return Format(JanBytes, target, offset); | |
case 2: return Format(FebBytes, target, offset); | |
case 3: return Format(MarBytes, target, offset); | |
case 4: return Format(AprBytes, target, offset); | |
case 5: return Format(MayBytes, target, offset); | |
case 6: return Format(JunBytes, target, offset); | |
case 7: return Format(JulBytes, target, offset); | |
case 8: return Format(AugBytes, target, offset); | |
case 9: return Format(SepBytes, target, offset); | |
case 10: return Format(OctBytes, target, offset); | |
case 11: return Format(NovBytes, target, offset); | |
case 12: return Format(DecBytes, target, offset); | |
default: return offset; | |
} | |
} | |
private static unsafe int FormatYear(int year, char* target, int offset) | |
{ | |
offset = Format(GetAsciiChar(year / 1000), target, offset); | |
offset = Format(GetAsciiChar(year % 1000 / 100), target, offset); | |
offset = Format(GetAsciiChar(year % 100 / 10), target, offset); | |
offset = Format(GetAsciiChar(year % 10), target, offset); | |
return offset; | |
} | |
private static unsafe int FormatTimeOfDay(TimeSpan timeOfDay, char* target, int offset) | |
{ | |
offset = FormatNumber(timeOfDay.Hours, target, offset); | |
offset = Format(Colon, target, offset); | |
offset = FormatNumber(timeOfDay.Minutes, target, offset); | |
offset = Format(Colon, target, offset); | |
offset = FormatNumber(timeOfDay.Seconds, target, offset); | |
return offset; | |
} | |
private static unsafe int FormatNumber(int number, char* target, int offset) | |
{ | |
offset = Format(GetAsciiChar(number / 10), target, offset); | |
offset = Format(GetAsciiChar(number % 10), target, offset); | |
return offset; | |
} | |
private static unsafe int Format(byte[] source, char* target, int offset) | |
{ | |
foreach (var b in source) | |
{ | |
offset = Format((char)b, target, offset); | |
} | |
return offset; | |
} | |
private static unsafe int Format(char value, char* target, int offset) | |
{ | |
target[offset++] = value; | |
return offset; | |
} | |
private static char GetAsciiChar(int value) | |
{ | |
return (char)(AsciiNumberOffset + value); | |
} | |
private static string GetDayName(DayOfWeek dayOfWeek) | |
{ | |
return FormatInfo.GetAbbreviatedDayName(dayOfWeek); | |
} | |
private static string GetMonthName(int month) | |
{ | |
return FormatInfo.GetAbbreviatedMonthName(month); | |
} | |
} | |
internal struct UtcOffset | |
{ | |
public DateTimeOffset DateTimeOffset { get; private set; } | |
public long Ticks { get; private set; } | |
public int Day { get; private set; } | |
public int DayOfWeek { get; private set; } | |
public int Month { get; private set; } | |
public int Year { get; private set; } | |
public UtcOffset(DateTimeOffset dateTimeOffset) | |
{ | |
DateTimeOffset = dateTimeOffset; | |
Ticks = dateTimeOffset.Date.Ticks; | |
Day = dateTimeOffset.Day; | |
DayOfWeek = (int)dateTimeOffset.DayOfWeek; | |
Month = dateTimeOffset.Month; | |
Year = dateTimeOffset.Year; | |
} | |
} | |
internal static class DateTimeFormatterTore | |
{ | |
private static readonly DateTimeFormatInfo FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat; | |
private static readonly string[] DayNames = FormatInfo.AbbreviatedDayNames; | |
private static readonly string[] MonthNames = FormatInfo.AbbreviatedMonthNames; | |
private static UtcOffset _utcOffset = new UtcOffset(DateTimeOffset.UtcNow); | |
// The format is "ddd, dd MMM yyyy HH:mm:ss GMT". | |
private const int Rfc1123DateLength = 29; | |
// ASCII numbers are in the range 48 - 57. | |
private const int AsciiNumberOffset = 0x30; | |
private const string Gmt = "GMT"; | |
private const char Comma = ','; | |
private const char Space = ' '; | |
private const char Colon = ':'; | |
public static unsafe string ToRfc1123String(this DateTimeOffset dateTime) | |
{ | |
var offset = 0; | |
char* target = stackalloc char[Rfc1123DateLength]; | |
var utcNow = DateTimeOffset.UtcNow; | |
if (dateTime.Offset > TimeSpan.Zero) | |
dateTime = dateTime.ToUniversalTime(); | |
if (_utcOffset.Ticks != utcNow.Date.Ticks) | |
_utcOffset = new UtcOffset(utcNow); | |
var utcOffset = _utcOffset; | |
if (utcOffset.Ticks != dateTime.Date.Ticks) | |
utcOffset = new UtcOffset(dateTime); | |
FormatDayOfWeek(utcOffset.DayOfWeek, target, ref offset); | |
Format(Comma, target, ref offset); | |
Format(Space, target, ref offset); | |
FormatNumber(utcOffset.Day, target, ref offset); | |
Format(Space, target, ref offset); | |
FormatMonth(utcOffset.Month, target, ref offset); | |
Format(Space, target, ref offset); | |
FormatYear(utcOffset.Year, target, ref offset); | |
Format(Space, target, ref offset); | |
FormatTimeOfDay(dateTime.Ticks - utcOffset.Ticks, target, ref offset); | |
Format(Space, target, ref offset); | |
Format(Gmt, target, ref offset); | |
return new string(target, 0, offset); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void FormatDayOfWeek(int dayOfWeek, char* target, ref int offset) | |
{ | |
Format(DayNames[dayOfWeek], target, ref offset); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void FormatMonth(int month, char* target, ref int offset) | |
{ | |
Format(MonthNames[month - 1], target, ref offset); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void FormatYear(int year, char* target, ref int offset) | |
{ | |
Format(GetAsciiChar(year / 1000), target, ref offset); | |
Format(GetAsciiChar(year % 1000 / 100), target, ref offset); | |
Format(GetAsciiChar(year % 100 / 10), target, ref offset); | |
Format(GetAsciiChar(year % 10), target, ref offset); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void FormatTimeOfDay(long ticks, char* target, ref int offset) | |
{ | |
var hours = ticks / TimeSpan.TicksPerHour; | |
var minutes = ticks % TimeSpan.TicksPerHour / TimeSpan.TicksPerMinute; | |
var seconds = ticks % TimeSpan.TicksPerMinute / TimeSpan.TicksPerSecond; | |
FormatNumber((int)hours, target, ref offset); | |
Format(Colon, target, ref offset); | |
FormatNumber((int)minutes, target, ref offset); | |
Format(Colon, target, ref offset); | |
FormatNumber((int)seconds, target, ref offset); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void FormatNumber(int number, char* target, ref int offset) | |
{ | |
Format(GetAsciiChar(number / 10), target, ref offset); | |
Format(GetAsciiChar(number % 10), target, ref offset); | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void Format(string source, char* target, ref int offset) | |
{ | |
for (var i = 0; i < source.Length; i++) | |
{ | |
target[offset++] = source[i]; | |
} | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static unsafe void Format(char value, char* target, ref int offset) | |
{ | |
target[offset++] = value; | |
} | |
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |
private static char GetAsciiChar(int value) | |
{ | |
return (char)(AsciiNumberOffset + value); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment