Skip to content
This repository was archived by the owner on Dec 24, 2022. It is now read-only.

Commit 53e9fa8

Browse files
Siliconrobmythz
authored andcommitted
DateTimes that are exported in ISO8601 are not consistent across serialization formats JSON/XML/CSV (#507)
* CSV DateTime formatting for midnight fix DateTimes that are exported in ISO8601 are not consistent across serialization for midnight on days. JSON and XML both show a datetime with the fractional seconds like so `2018-02-10T00:00:00.0000000Z` but CSV will produce a DateTime with out the any of the timespan portion of timezone indicator i.e. `2018-01-01` this is also not consistent with the default ISO8601 parser in .NET for DateTime called as so `.ToString("o")` will produce the ISO8601 serialized times as for JSON and XML, but not CSV. Adding the ability to specify a DateTimeFormat that will allow a user to override the DateTimeFormat defaults so serialized value is consistent * Remove unused System.Globalization reference
1 parent 7dd879d commit 53e9fa8

File tree

4 files changed

+74
-1
lines changed

4 files changed

+74
-1
lines changed

src/ServiceStack.Text/Common/DateTimeSerializer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,8 +443,12 @@ public static TimeSpan ParseXsdTimeSpan(string dateTimeStr)
443443
public static string ToShortestXsdDateTimeString(DateTime dateTime)
444444
{
445445
dateTime = dateTime.UseConfigSpecifiedSetting();
446-
var timeOfDay = dateTime.TimeOfDay;
446+
if (!string.IsNullOrEmpty(JsConfig.DateTimeFormat))
447+
{
448+
return dateTime.ToString(JsConfig.DateTimeFormat, CultureInfo.InvariantCulture);
449+
}
447450

451+
var timeOfDay = dateTime.TimeOfDay;
448452
var isStartOfDay = timeOfDay.Ticks == 0;
449453
if (isStartOfDay && !JsConfig.SkipDateTimeConversion)
450454
return dateTime.ToString(ShortDateTimeFormat, CultureInfo.InvariantCulture);

src/ServiceStack.Text/JsConfig.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ public static JsConfigScope With(
222222
bool? preferInterfaces = null,
223223
bool? throwOnDeserializationError = null,
224224
string typeAttr = null,
225+
string dateTimeFormat = null,
225226
Func<Type, string> typeWriter = null,
226227
Func<string, Type> typeFinder = null,
227228
bool? treatEnumAsInteger = null,
@@ -258,6 +259,7 @@ public static JsConfigScope With(
258259
PropertyConvention = propertyConvention ?? sPropertyConvention,
259260
PreferInterfaces = preferInterfaces ?? sPreferInterfaces,
260261
ThrowOnDeserializationError = throwOnDeserializationError ?? sThrowOnDeserializationError,
262+
DateTimeFormat = dateTimeFormat ?? sDateTimeFormat,
261263
TypeAttr = typeAttr ?? sTypeAttr,
262264
TypeWriter = typeWriter ?? sTypeWriter,
263265
TypeFinder = typeFinder ?? sTypeFinder,
@@ -274,6 +276,21 @@ public static JsConfigScope With(
274276
};
275277
}
276278

279+
private static string sDateTimeFormat;
280+
public static string DateTimeFormat
281+
{
282+
get
283+
{
284+
return (JsConfigScope.Current != null ? JsConfigScope.Current.DateTimeFormat : null)
285+
?? sDateTimeFormat;
286+
//?? DateTimeSerializer.XsdDateTimeFormatSeconds;
287+
}
288+
set
289+
{
290+
if (sDateTimeFormat == null) sDateTimeFormat = value;
291+
}
292+
}
293+
277294
private static bool? sConvertObjectTypesIntoStringDictionary;
278295
public static bool ConvertObjectTypesIntoStringDictionary
279296
{
@@ -974,6 +991,7 @@ public static void Reset()
974991
sPreferInterfaces = null;
975992
sThrowOnDeserializationError = null;
976993
sTypeAttr = null;
994+
sDateTimeFormat = null;
977995
sJsonTypeAttrInObject = null;
978996
sJsvTypeAttrInObject = null;
979997
sTypeWriter = null;

src/ServiceStack.Text/JsConfigScope.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public void Dispose()
6161
public bool? ExcludeTypeInfo { get; set; }
6262
public bool? IncludeTypeInfo { get; set; }
6363
public string TypeAttr { get; set; }
64+
public string DateTimeFormat { get; set; }
6465
internal string JsonTypeAttrInObject { get; set; }
6566
internal string JsvTypeAttrInObject { get; set; }
6667
public Func<Type, string> TypeWriter { get; set; }

tests/ServiceStack.Text.Tests/CsvTests/ObjectSerializerTests.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,56 @@ namespace ServiceStack.Text.Tests.CsvTests
88
[TestFixture]
99
public class ObjectSerializerTests
1010
{
11+
[Test]
12+
public void MidnightAndNoonTestSerialization()
13+
{
14+
JsConfig.Reset();
15+
JsConfig<DateTime>.SerializeFn = null;
16+
JsConfig<DateTime>.Reset();
17+
18+
JsConfig.AlwaysUseUtc = true;
19+
JsConfig.AssumeUtc = true;
20+
// Set the format for DatTimeFormatting explicitly using DateTimeSerializer.XsdDateTimeFormat because it is ISO8601 fractional seconds
21+
JsConfig.DateTimeFormat = DateTimeSerializer.XsdDateTimeFormat;
22+
23+
var midnight = new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc);
24+
var noon = midnight.AddHours(12);
25+
var dotnetValues = new
26+
{
27+
Midnight = midnight.ToString("o"),
28+
Noon = noon.ToString("o")
29+
};
30+
var data = new object[] {
31+
new POCO { DateTime = midnight },
32+
new POCO { DateTime = noon }
33+
};
34+
var csv = CsvSerializer.SerializeToCsv(data);
35+
// Reset back to defaults
36+
JsConfig.Reset();
37+
JsConfig<DateTime>.SerializeFn = null;
38+
JsConfig<DateTime>.Reset();
39+
40+
Console.WriteLine(csv);
41+
42+
const string endLineChars = "\r\n";
43+
Assert.AreEqual($"DateTime{endLineChars}" +
44+
$"{dotnetValues.Midnight}{endLineChars}" +
45+
$"{dotnetValues.Noon}{endLineChars}", csv);
46+
47+
// Now don't use custom DateTimeFormat
48+
JsConfig.AlwaysUseUtc = true;
49+
JsConfig.AssumeUtc = true;
50+
csv = CsvSerializer.SerializeToCsv(data);
51+
Console.WriteLine(csv);
52+
Assert.AreEqual($"DateTime{endLineChars}" +
53+
$"2018-01-01{endLineChars}" +
54+
$"2018-01-01T12:00:00Z{endLineChars}", csv);
55+
56+
JsConfig.Reset();
57+
JsConfig<DateTime>.SerializeFn = null;
58+
JsConfig<DateTime>.Reset();
59+
}
60+
1161
[Test]
1262
public void IEnumerableObjectSerialization()
1363
{

0 commit comments

Comments
 (0)