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

Commit a752f2a

Browse files
committed
Handle yyyyMMdd date formats and allow handling fallbacks on DateTimeSerializer.OnParseErrorFn
1 parent 3bed251 commit a752f2a

File tree

2 files changed

+91
-52
lines changed

2 files changed

+91
-52
lines changed

src/ServiceStack.Text/Common/DateTimeSerializer.cs

Lines changed: 70 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ namespace ServiceStack.Text.Common
2121
{
2222
public static class DateTimeSerializer
2323
{
24+
public const string CondensedDateTimeFormat = "yyyyMMdd"; //8
2425
public const string ShortDateTimeFormat = "yyyy-MM-dd"; //11
2526
public const string DefaultDateTimeFormat = "dd/MM/yyyy HH:mm:ss"; //20
2627
public const string DefaultDateTimeFormatWithFraction = "dd/MM/yyyy HH:mm:ss.fff"; //24
@@ -40,6 +41,9 @@ public static class DateTimeSerializer
4041
private const char XsdTimeSeparator = 'T';
4142
private static readonly int XsdTimeSeparatorIndex = XsdDateTimeFormat.IndexOf(XsdTimeSeparator);
4243
private const string XsdUtcSuffix = "Z";
44+
private static readonly char[] DateTimeSeperators = new[] { '-', '/' };
45+
46+
public static Func<string, Exception, DateTime> OnParseErrorFn { get; set; }
4347

4448
/// <summary>
4549
/// If AlwaysUseUtc is set to true then convert all DateTime to UTC.
@@ -70,69 +74,84 @@ public static DateTime ParseRFC1123DateTime(string dateTimeStr)
7074

7175
public static DateTime ParseShortestXsdDateTime(string dateTimeStr)
7276
{
73-
if (string.IsNullOrEmpty(dateTimeStr))
74-
return DateTime.MinValue;
77+
try
78+
{
79+
if (string.IsNullOrEmpty(dateTimeStr))
80+
return DateTime.MinValue;
7581

76-
if (dateTimeStr.StartsWith(EscapedWcfJsonPrefix, StringComparison.Ordinal) || dateTimeStr.StartsWith(WcfJsonPrefix, StringComparison.Ordinal))
77-
return ParseWcfJsonDate(dateTimeStr).Prepare();
82+
if (dateTimeStr.StartsWith(EscapedWcfJsonPrefix, StringComparison.Ordinal) || dateTimeStr.StartsWith(WcfJsonPrefix, StringComparison.Ordinal))
83+
return ParseWcfJsonDate(dateTimeStr).Prepare();
7884

79-
if (dateTimeStr.Length == DefaultDateTimeFormat.Length
80-
|| dateTimeStr.Length == DefaultDateTimeFormatWithFraction.Length)
81-
{
82-
var unspecifiedDate = DateTime.Parse(dateTimeStr, CultureInfo.InvariantCulture);
83-
if (JsConfig.AssumeUtc)
84-
unspecifiedDate = DateTime.SpecifyKind(unspecifiedDate, DateTimeKind.Utc);
85+
if (dateTimeStr.Length == DefaultDateTimeFormat.Length
86+
|| dateTimeStr.Length == DefaultDateTimeFormatWithFraction.Length)
87+
{
88+
var unspecifiedDate = DateTime.Parse(dateTimeStr, CultureInfo.InvariantCulture);
89+
if (JsConfig.AssumeUtc)
90+
unspecifiedDate = DateTime.SpecifyKind(unspecifiedDate, DateTimeKind.Utc);
8591

86-
return unspecifiedDate.Prepare();
87-
}
92+
return unspecifiedDate.Prepare();
93+
}
8894

89-
switch (JsConfig.DateHandler)
90-
{
91-
case DateHandler.UnixTime:
92-
int unixTime;
93-
if (int.TryParse(dateTimeStr, out unixTime))
94-
return unixTime.FromUnixTime();
95-
break;
96-
case DateHandler.UnixTimeMs:
97-
long unixTimeMs;
98-
if (long.TryParse(dateTimeStr, out unixTimeMs))
99-
return unixTimeMs.FromUnixTimeMs();
100-
break;
101-
}
95+
switch (JsConfig.DateHandler)
96+
{
97+
case DateHandler.UnixTime:
98+
int unixTime;
99+
if (int.TryParse(dateTimeStr, out unixTime))
100+
return unixTime.FromUnixTime();
101+
break;
102+
case DateHandler.UnixTimeMs:
103+
long unixTimeMs;
104+
if (long.TryParse(dateTimeStr, out unixTimeMs))
105+
return unixTimeMs.FromUnixTimeMs();
106+
break;
107+
}
102108

103-
dateTimeStr = RepairXsdTimeSeparator(dateTimeStr);
109+
dateTimeStr = RepairXsdTimeSeparator(dateTimeStr);
104110

105-
if (dateTimeStr.Length == XsdDateTimeFormatSeconds.Length)
106-
return DateTime.ParseExact(dateTimeStr, XsdDateTimeFormatSeconds, null, DateTimeStyles.AdjustToUniversal).Prepare(parsedAsUtc:true);
111+
if (dateTimeStr.Length == XsdDateTimeFormatSeconds.Length)
112+
return DateTime.ParseExact(dateTimeStr, XsdDateTimeFormatSeconds, null, DateTimeStyles.AdjustToUniversal).Prepare(parsedAsUtc: true);
107113

108-
if (dateTimeStr.Length >= XsdDateTimeFormat3F.Length
109-
&& dateTimeStr.Length <= XsdDateTimeFormat.Length
110-
&& dateTimeStr.EndsWith(XsdUtcSuffix))
111-
{
112-
var dateTime = Env.IsMono ? ParseManual(dateTimeStr) : null;
113-
if (dateTime != null)
114-
return dateTime.Value;
114+
if (dateTimeStr.Length >= XsdDateTimeFormat3F.Length
115+
&& dateTimeStr.Length <= XsdDateTimeFormat.Length
116+
&& dateTimeStr.EndsWith(XsdUtcSuffix))
117+
{
118+
var dateTime = Env.IsMono ? ParseManual(dateTimeStr) : null;
119+
if (dateTime != null)
120+
return dateTime.Value;
115121

116-
return PclExport.Instance.ParseXsdDateTimeAsUtc(dateTimeStr);
117-
}
122+
return PclExport.Instance.ParseXsdDateTimeAsUtc(dateTimeStr);
123+
}
118124

119-
if (dateTimeStr.Length == ShortDateTimeFormat.Length)
120-
{
121-
var manualDate = ParseManual(dateTimeStr);
122-
if (manualDate != null)
123-
return manualDate.Value;
124-
}
125+
if (dateTimeStr.Length == CondensedDateTimeFormat.Length && dateTimeStr.IndexOfAny(DateTimeSeperators) == -1)
126+
{
127+
return DateTime.ParseExact(dateTimeStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
128+
}
125129

126-
try
127-
{
128-
var dateTime = DateTime.Parse(dateTimeStr, null, DateTimeStyles.AssumeLocal);
129-
return dateTime.Prepare();
130+
if (dateTimeStr.Length == ShortDateTimeFormat.Length)
131+
{
132+
var manualDate = ParseManual(dateTimeStr);
133+
if (manualDate != null)
134+
return manualDate.Value;
135+
}
136+
137+
try
138+
{
139+
var dateTime = DateTime.Parse(dateTimeStr, null, DateTimeStyles.AssumeLocal);
140+
return dateTime.Prepare();
141+
}
142+
catch (FormatException)
143+
{
144+
var manualDate = ParseManual(dateTimeStr);
145+
if (manualDate != null)
146+
return manualDate.Value;
147+
148+
throw;
149+
}
130150
}
131-
catch (FormatException)
151+
catch (Exception ex)
132152
{
133-
var manualDate = ParseManual(dateTimeStr);
134-
if (manualDate != null)
135-
return manualDate.Value;
153+
if (OnParseErrorFn != null)
154+
return OnParseErrorFn(dateTimeStr, ex);
136155

137156
throw;
138157
}

tests/ServiceStack.Text.Tests/Utils/DateTimeSerializerTests.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Globalization;
23
using System.Xml;
34
using NUnit.Framework;
45
using ServiceStack.Text.Common;
@@ -568,7 +569,26 @@ public void Can_deserialize_LocalTime_using_default_DateHandler()
568569
var dateStr = date.ToJson();
569570
var fromDate = dateStr.FromJson<DateTime>();
570571
Assert.AreEqual(date.ToLocalTime().RoundToSecond(), fromDate.ToLocalTime().RoundToSecond());
571-
}
572+
}
573+
574+
[Test]
575+
public void Can_handle_condensed_date_format()
576+
{
577+
var date = "20001213".FromJson<DateTime>();
578+
Assert.That(date, Is.EqualTo(new DateTime(2000, 12, 13)));
579+
}
580+
581+
[Test]
582+
public void Can_handle_invalid_format_Exceptions()
583+
{
584+
DateTimeSerializer.OnParseErrorFn = (str, ex) =>
585+
DateTime.ParseExact(str, "yyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None);
586+
587+
var date = "001213".FromJson<DateTime>();
588+
Assert.That(date, Is.EqualTo(new DateTime(2000, 12, 13)));
589+
590+
DateTimeSerializer.OnParseErrorFn = null;
591+
}
572592
}
573593

574594
}

0 commit comments

Comments
 (0)