Skip to content
This repository was archived by the owner on Nov 20, 2018. It is now read-only.

Commit df88a1c

Browse files
committed
Added custom DateTimeFormatter
1 parent 067eb9c commit df88a1c

File tree

7 files changed

+156
-9
lines changed

7 files changed

+156
-9
lines changed

src/Microsoft.Net.Http.Headers/ContentDispositionHeaderValue.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ private void SetDate(string parameter, DateTimeOffset? date)
321321
else
322322
{
323323
// Must always be quoted
324-
var dateString = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", HttpRuleParser.DateToString(date.Value));
324+
var dateString = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", HeaderUtilities.FormatDate(date.Value));
325325
if (dateParameter != null)
326326
{
327327
dateParameter.Value = dateString;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.Globalization;
6+
using System.Runtime.CompilerServices;
7+
8+
namespace Microsoft.Net.Http.Headers
9+
{
10+
internal static class DateTimeFormatter
11+
{
12+
private static readonly DateTimeFormatInfo FormatInfo = CultureInfo.InvariantCulture.DateTimeFormat;
13+
14+
private static readonly string[] MonthNames = FormatInfo.AbbreviatedMonthNames;
15+
private static readonly string[] DayNames = FormatInfo.AbbreviatedDayNames;
16+
17+
private static readonly int Rfc1123DateLength = "ddd, dd MMM yyyy HH:mm:ss GMT".Length;
18+
19+
// ASCII numbers are in the range 48 - 57.
20+
private const int AsciiNumberOffset = 0x30;
21+
22+
private const string Gmt = "GMT";
23+
private const char Comma = ',';
24+
private const char Space = ' ';
25+
private const char Colon = ':';
26+
27+
public static unsafe string ToRfc1123String(this DateTimeOffset dateTime)
28+
{
29+
var offset = 0;
30+
var universal = dateTime.UtcDateTime;
31+
char* target = stackalloc char[Rfc1123DateLength];
32+
33+
FormatDayOfWeek(universal.DayOfWeek, target, ref offset);
34+
Format(Comma, target, ref offset);
35+
Format(Space, target, ref offset);
36+
FormatNumber(universal.Day, target, ref offset);
37+
Format(Space, target, ref offset);
38+
FormatMonth(universal.Month, target, ref offset);
39+
Format(Space, target, ref offset);
40+
FormatYear(universal.Year, target, ref offset);
41+
Format(Space, target, ref offset);
42+
FormatTimeOfDay(universal.TimeOfDay, target, ref offset);
43+
Format(Space, target, ref offset);
44+
Format(Gmt, target, ref offset);
45+
46+
return new string(target, 0, offset);
47+
}
48+
49+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
50+
private static unsafe void FormatDayOfWeek(DayOfWeek dayOfWeek, char* target, ref int offset)
51+
{
52+
Format(DayNames[(int)dayOfWeek], target, ref offset);
53+
}
54+
55+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
56+
private static unsafe void FormatMonth(int month, char* target, ref int offset)
57+
{
58+
Format(MonthNames[month - 1], target, ref offset);
59+
}
60+
61+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
62+
private static unsafe void FormatYear(int year, char* target, ref int offset)
63+
{
64+
Format(GetAsciiChar(year / 1000), target, ref offset);
65+
Format(GetAsciiChar(year % 1000 / 100), target, ref offset);
66+
Format(GetAsciiChar(year % 100 / 10), target, ref offset);
67+
Format(GetAsciiChar(year % 10), target, ref offset);
68+
}
69+
70+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
71+
private static unsafe void FormatTimeOfDay(TimeSpan timeOfDay, char* target, ref int offset)
72+
{
73+
FormatNumber(timeOfDay.Hours, target, ref offset);
74+
Format(Colon, target, ref offset);
75+
FormatNumber(timeOfDay.Minutes, target, ref offset);
76+
Format(Colon, target, ref offset);
77+
FormatNumber(timeOfDay.Seconds, target, ref offset);
78+
}
79+
80+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
81+
private static unsafe void FormatNumber(int number, char* target, ref int offset)
82+
{
83+
Format(GetAsciiChar(number / 10), target, ref offset);
84+
Format(GetAsciiChar(number % 10), target, ref offset);
85+
}
86+
87+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
88+
private static unsafe void Format(string source, char* target, ref int offset)
89+
{
90+
for (var i = 0; i < source.Length; i++)
91+
{
92+
target[offset++] = source[i];
93+
}
94+
}
95+
96+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
97+
private static unsafe void Format(char value, char* target, ref int offset)
98+
{
99+
target[offset++] = value;
100+
}
101+
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
private static char GetAsciiChar(int value)
104+
{
105+
return (char)(AsciiNumberOffset + value);
106+
}
107+
}
108+
}

src/Microsoft.Net.Http.Headers/HeaderUtilities.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public static bool TryParseDate(string input, out DateTimeOffset result)
220220

221221
public static string FormatDate(DateTimeOffset dateTime)
222222
{
223-
return HttpRuleParser.DateToString(dateTime);
223+
return dateTime.ToRfc1123String();
224224
}
225225

226226
public static string RemoveQuotes(string input)

src/Microsoft.Net.Http.Headers/HttpRuleParser.cs

-6
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,6 @@ internal static HttpParseResult GetQuotedPairLength(string input, int startIndex
233233
return HttpParseResult.Parsed;
234234
}
235235

236-
internal static string DateToString(DateTimeOffset dateTime)
237-
{
238-
// Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo)
239-
return dateTime.ToUniversalTime().ToString("r", CultureInfo.InvariantCulture);
240-
}
241-
242236
internal static bool TryStringToDate(string input, out DateTimeOffset result)
243237
{
244238
// Try the various date formats in the order listed above.

src/Microsoft.Net.Http.Headers/RangeConditionHeaderValue.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public override string ToString()
5353
{
5454
if (_entityTag == null)
5555
{
56-
return HttpRuleParser.DateToString(_lastModified.Value);
56+
return HeaderUtilities.FormatDate(_lastModified.Value);
5757
}
5858
return _entityTag.ToString();
5959
}

src/Microsoft.Net.Http.Headers/project.json

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
},
1313
"buildOptions": {
1414
"warningsAsErrors": true,
15+
"allowUnsafe": true,
1516
"keyFile": "../../tools/Key.snk",
1617
"nowarn": [
1718
"CS1591"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Xunit;
6+
7+
namespace Microsoft.Net.Http.Headers
8+
{
9+
public static class HeaderUtilitiesTest
10+
{
11+
private const string Rfc1123Format = "r";
12+
13+
[Theory]
14+
[MemberData(nameof(TestValues))]
15+
public static void ReturnsSameResultAsRfc1123String(DateTimeOffset dateTime)
16+
{
17+
var expected = dateTime.ToString(Rfc1123Format);
18+
var actual = HeaderUtilities.FormatDate(dateTime);
19+
20+
Assert.Equal(expected, actual);
21+
}
22+
23+
public static TheoryData<DateTimeOffset> TestValues
24+
{
25+
get
26+
{
27+
var data = new TheoryData<DateTimeOffset>();
28+
29+
var now = DateTimeOffset.Now;
30+
31+
for (var i = 0; i < 60; i++)
32+
{
33+
data.Add(now.AddSeconds(i));
34+
data.Add(now.AddMinutes(i));
35+
data.Add(now.AddDays(i));
36+
data.Add(now.AddMonths(i));
37+
data.Add(now.AddYears(i));
38+
}
39+
40+
return data;
41+
}
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)