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

Commit 6463f00

Browse files
committed
Add support for .NET 6 DateOnly/TimeOnly data types
1 parent 67d2314 commit 6463f00

File tree

9 files changed

+269
-7
lines changed

9 files changed

+269
-7
lines changed

src/ServiceStack.Text/Common/DeserializeBuiltin.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ private static ParseStringSpanDelegate GetParseStringSpanFn()
7474
return value => DateTimeSerializer.ParseDateTimeOffset(value.ToString());
7575
if (typeof(T) == typeof(TimeSpan))
7676
return value => DateTimeSerializer.ParseTimeSpan(value.ToString());
77+
#if NET6_0
78+
if (typeof(T) == typeof(DateOnly))
79+
return value => DateOnly.FromDateTime(DateTimeSerializer.ParseShortestXsdDateTime(value.ToString()));
80+
if (typeof(T) == typeof(TimeOnly))
81+
return value => TimeOnly.FromTimeSpan(DateTimeSerializer.ParseTimeSpan(value.ToString()));
82+
#endif
7783
}
7884
else
7985
{
@@ -119,6 +125,12 @@ private static ParseStringSpanDelegate GetParseStringSpanFn()
119125
return value => value.IsNullOrEmpty() ? (Guid?)null : value.ParseGuid();
120126
if (typeof(T) == typeof(DateTimeOffset?))
121127
return value => DateTimeSerializer.ParseNullableDateTimeOffset(value.ToString());
128+
#if NET6_0
129+
if (typeof(T) == typeof(DateOnly?))
130+
return value => value.IsNullOrEmpty() ? default : DateOnly.FromDateTime(DateTimeSerializer.ParseShortestXsdDateTime(value.ToString()));
131+
if (typeof(T) == typeof(TimeOnly?))
132+
return value => value.IsNullOrEmpty() ? default : TimeOnly.FromTimeSpan(DateTimeSerializer.ParseTimeSpan(value.ToString()));
133+
#endif
122134
}
123135

124136
return null;

src/ServiceStack.Text/Common/ITypeSerializer.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public interface ITypeSerializer
3030
void WriteNullableDateTime(TextWriter writer, object dateTime);
3131
void WriteDateTimeOffset(TextWriter writer, object oDateTimeOffset);
3232
void WriteNullableDateTimeOffset(TextWriter writer, object dateTimeOffset);
33-
void WriteTimeSpan(TextWriter writer, object dateTimeOffset);
34-
void WriteNullableTimeSpan(TextWriter writer, object dateTimeOffset);
33+
void WriteTimeSpan(TextWriter writer, object timeSpan);
34+
void WriteNullableTimeSpan(TextWriter writer, object timeSpan);
3535
void WriteGuid(TextWriter writer, object oValue);
3636
void WriteNullableGuid(TextWriter writer, object oValue);
3737
void WriteBytes(TextWriter writer, object oByteValue);
@@ -50,6 +50,13 @@ public interface ITypeSerializer
5050
void WriteDecimal(TextWriter writer, object decimalValue);
5151
void WriteEnum(TextWriter writer, object enumValue);
5252

53+
#if NET6_0_OR_GREATER
54+
void WriteDateOnly(TextWriter writer, object oDateOnly);
55+
void WriteNullableDateOnly(TextWriter writer, object oDateOnly);
56+
void WriteTimeOnly(TextWriter writer, object oTimeOnly);
57+
void WriteNullableTimeOnly(TextWriter writer, object oTimeOnly);
58+
#endif
59+
5360
ParseStringDelegate GetParseFn<T>();
5461
ParseStringSpanDelegate GetParseStringSpanFn<T>();
5562
ParseStringDelegate GetParseFn(Type type);

src/ServiceStack.Text/Common/JsWriter.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,24 @@ public WriteObjectDelegate GetValueTypeToStringMethod(Type type)
278278

279279
if (type == typeof(Guid?))
280280
return Serializer.WriteNullableGuid;
281+
282+
#if NET6_0
283+
if (type == typeof(DateOnly))
284+
if (isNullable)
285+
return Serializer.WriteNullableDateOnly;
286+
else
287+
return Serializer.WriteDateOnly;
288+
if (type == typeof(DateOnly?))
289+
return Serializer.WriteDateOnly;
290+
291+
if (type == typeof(TimeOnly))
292+
if (isNullable)
293+
return Serializer.WriteNullableTimeOnly;
294+
else
295+
return Serializer.WriteTimeOnly;
296+
if (type == typeof(TimeOnly?))
297+
return Serializer.WriteTimeOnly;
298+
#endif
281299
}
282300
else
283301
{

src/ServiceStack.Text/DateTimeExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ public static long ToUnixTimeMs(this long ticks)
7676
return (ticks - UnixEpoch) / TimeSpan.TicksPerMillisecond;
7777
}
7878

79+
#if NET6_0
80+
public static long ToUnixTimeMs(this DateOnly dateOnly) => dateOnly.ToDateTime(default, DateTimeKind.Utc).ToUnixTimeMs();
81+
public static long ToUnixTime(this DateOnly dateOnly) => dateOnly.ToDateTime(default, DateTimeKind.Utc).ToUnixTime();
82+
#endif
83+
7984
public static DateTime FromUnixTimeMs(this double msSince1970)
8085
{
8186
return UnixEpochDateTimeUtc + TimeSpan.FromMilliseconds(msSince1970);

src/ServiceStack.Text/Json/JsonTypeSerializer.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ public void WriteTimeSpan(TextWriter writer, object oTimeSpan)
138138

139139
public void WriteNullableTimeSpan(TextWriter writer, object oTimeSpan)
140140
{
141-
142141
if (oTimeSpan == null) return;
143142
WriteTimeSpan(writer, ((TimeSpan?)oTimeSpan).Value);
144143
}
@@ -289,6 +288,50 @@ public void WriteEnum(TextWriter writer, object enumValue)
289288
JsWriter.WriteEnumFlags(writer, enumValue);
290289
}
291290

291+
292+
#if NET6_0
293+
public void WriteDateOnly(TextWriter writer, object oDateOnly)
294+
{
295+
var dateOnly = (DateOnly)oDateOnly;
296+
switch (JsConfig.DateHandler)
297+
{
298+
case DateHandler.UnixTime:
299+
writer.Write(dateOnly.ToUnixTime());
300+
break;
301+
case DateHandler.UnixTimeMs:
302+
writer.Write(dateOnly.ToUnixTimeMs());
303+
break;
304+
default:
305+
writer.Write(JsWriter.QuoteString);
306+
writer.Write(dateOnly.ToString("O"));
307+
writer.Write(JsWriter.QuoteString);
308+
break;
309+
}
310+
}
311+
312+
public void WriteNullableDateOnly(TextWriter writer, object oDateOnly)
313+
{
314+
if (oDateOnly == null)
315+
writer.Write(JsonUtils.Null);
316+
else
317+
WriteDateOnly(writer, oDateOnly);
318+
}
319+
320+
public void WriteTimeOnly(TextWriter writer, object oTimeOnly)
321+
{
322+
var stringValue = JsConfig.TimeSpanHandler == TimeSpanHandler.StandardFormat
323+
? oTimeOnly.ToString()
324+
: DateTimeSerializer.ToXsdTimeSpanString(((TimeOnly)oTimeOnly).ToTimeSpan());
325+
WriteRawString(writer, stringValue);
326+
}
327+
328+
public void WriteNullableTimeOnly(TextWriter writer, object oTimeOnly)
329+
{
330+
if (oTimeOnly == null) return;
331+
WriteTimeSpan(writer, ((TimeOnly?)oTimeOnly).Value.ToTimeSpan());
332+
}
333+
#endif
334+
292335
[MethodImpl(MethodImplOptions.AggressiveInlining)]
293336
public ParseStringDelegate GetParseFn<T>()
294337
{

src/ServiceStack.Text/Jsv/JsvTypeSerializer.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,45 @@ public void WriteEnum(TextWriter writer, object enumValue)
248248
JsWriter.WriteEnumFlags(writer, enumValue);
249249
}
250250

251+
#if NET6_0
252+
public void WriteDateOnly(TextWriter writer, object oDateOnly)
253+
{
254+
var dateOnly = (DateOnly)oDateOnly;
255+
switch (JsConfig.DateHandler)
256+
{
257+
case DateHandler.UnixTime:
258+
writer.Write(dateOnly.ToUnixTime());
259+
break;
260+
case DateHandler.UnixTimeMs:
261+
writer.Write(dateOnly.ToUnixTimeMs());
262+
break;
263+
default:
264+
writer.Write(dateOnly.ToString("O"));
265+
break;
266+
}
267+
}
268+
269+
public void WriteNullableDateOnly(TextWriter writer, object oDateOnly)
270+
{
271+
if (oDateOnly == null) return;
272+
WriteDateOnly(writer, oDateOnly);
273+
}
274+
275+
public void WriteTimeOnly(TextWriter writer, object oTimeOnly)
276+
{
277+
var stringValue = JsConfig.TimeSpanHandler == TimeSpanHandler.StandardFormat
278+
? oTimeOnly.ToString()
279+
: DateTimeSerializer.ToXsdTimeSpanString(((TimeOnly)oTimeOnly).ToTimeSpan());
280+
WriteRawString(writer, stringValue);
281+
}
282+
283+
public void WriteNullableTimeOnly(TextWriter writer, object oTimeOnly)
284+
{
285+
if (oTimeOnly == null) return;
286+
WriteTimeSpan(writer, ((TimeOnly?)oTimeOnly).Value.ToTimeSpan());
287+
}
288+
#endif
289+
251290
public ParseStringDelegate GetParseFn<T>() => JsvReader.Instance.GetParseFn<T>();
252291

253292
public ParseStringDelegate GetParseFn(Type type) => JsvReader.GetParseFn(type);
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
using System;
2+
using NUnit.Framework;
3+
4+
#if NET6_0
5+
namespace ServiceStack.Text.Tests;
6+
7+
public class DateOnlyDto
8+
{
9+
public DateOnly Date { get; set; }
10+
11+
protected bool Equals(DateOnlyDto other) => Date.Equals(other.Date);
12+
public override bool Equals(object obj)
13+
{
14+
if (ReferenceEquals(null, obj)) return false;
15+
if (ReferenceEquals(this, obj)) return true;
16+
if (obj.GetType() != this.GetType()) return false;
17+
return Equals((DateOnlyDto)obj);
18+
}
19+
public override int GetHashCode() => Date.GetHashCode();
20+
}
21+
22+
public class TimeOnlyDto
23+
{
24+
public TimeOnly Time { get; set; }
25+
26+
protected bool Equals(TimeOnlyDto other) => Time.Equals(other.Time);
27+
public override bool Equals(object obj)
28+
{
29+
if (ReferenceEquals(null, obj)) return false;
30+
if (ReferenceEquals(this, obj)) return true;
31+
if (obj.GetType() != this.GetType()) return false;
32+
return Equals((TimeOnlyDto)obj);
33+
}
34+
public override int GetHashCode() => Time.GetHashCode();
35+
}
36+
37+
public class NullableDateOnlyDto
38+
{
39+
public DateOnly? Date { get; set; }
40+
41+
protected bool Equals(NullableDateOnlyDto other) => Date.Equals(other.Date);
42+
public override bool Equals(object obj)
43+
{
44+
if (ReferenceEquals(null, obj)) return false;
45+
if (ReferenceEquals(this, obj)) return true;
46+
if (obj.GetType() != this.GetType()) return false;
47+
return Equals((NullableDateOnlyDto)obj);
48+
}
49+
public override int GetHashCode() => (Date ?? default).GetHashCode();
50+
}
51+
52+
public class NullableTimeOnlyDto
53+
{
54+
public TimeOnly? Time { get; set; }
55+
56+
protected bool Equals(NullableTimeOnlyDto other) => Time.Equals(other.Time);
57+
public override bool Equals(object obj)
58+
{
59+
if (ReferenceEquals(null, obj)) return false;
60+
if (ReferenceEquals(this, obj)) return true;
61+
if (obj.GetType() != this.GetType()) return false;
62+
return Equals((NullableTimeOnlyDto)obj);
63+
}
64+
public override int GetHashCode() => (Time ?? default).GetHashCode();
65+
}
66+
67+
public class Net6TypeTests
68+
{
69+
[Test]
70+
public void Can_serialize_DateOnly()
71+
{
72+
var date = new DateOnly(2001, 1, 13);
73+
var json = date.ToJson();
74+
Assert.That(json, Is.EqualTo("\"2001-01-13\""));
75+
76+
var fromJson = json.FromJson<DateOnly>();
77+
Assert.That(fromJson, Is.EqualTo(date));
78+
79+
var dto = new DateOnlyDto { Date = date };
80+
json = dto.ToJson();
81+
Assert.That(json, Is.EqualTo("{\"Date\":\"2001-01-13\"}"));
82+
var fromJsonDto = json.FromJson<DateOnlyDto>();
83+
Assert.That(fromJsonDto, Is.EqualTo(dto));
84+
85+
var nullableDto = new NullableDateOnlyDto { Date = date };
86+
json = nullableDto.ToJson();
87+
Assert.That(json, Is.EqualTo("{\"Date\":\"2001-01-13\"}"));
88+
var fromJsonNullableDto = json.FromJson<NullableDateOnlyDto>();
89+
Assert.That(fromJsonNullableDto, Is.EqualTo(nullableDto));
90+
}
91+
92+
[Test]
93+
public void Can_serialize_DateOnly_UnixTime()
94+
{
95+
using (JsConfig.With(new Config { DateHandler = DateHandler.UnixTime }))
96+
{
97+
var date = new DateOnly(2001, 1, 13);
98+
var json = date.ToJson();
99+
Assert.That(json, Is.EqualTo("979344000"));
100+
101+
var fromJson = json.FromJson<DateOnly>();
102+
Assert.That(fromJson, Is.EqualTo(date));
103+
104+
var dto = new DateOnlyDto { Date = date };
105+
json = dto.ToJson();
106+
Assert.That(json, Is.EqualTo("{\"Date\":979344000}"));
107+
108+
var nullableDto = new NullableDateOnlyDto { Date = date };
109+
json = nullableDto.ToJson();
110+
Assert.That(json, Is.EqualTo("{\"Date\":979344000}"));
111+
}
112+
}
113+
114+
[Test]
115+
public void Can_serialize_TimeOnly()
116+
{
117+
var time = new TimeOnly(13, 13, 13);
118+
var json = time.ToJson();
119+
Assert.That(json, Is.EqualTo("\"PT13H13M13S\""));
120+
121+
var fromJson = json.FromJson<TimeOnly>();
122+
Assert.That(fromJson, Is.EqualTo(time));
123+
124+
var dto = new TimeOnlyDto { Time = time };
125+
json = dto.ToJson();
126+
Assert.That(json, Is.EqualTo("{\"Time\":\"PT13H13M13S\"}"));
127+
var fromJsonDto = json.FromJson<TimeOnlyDto>();
128+
Assert.That(fromJsonDto, Is.EqualTo(dto));
129+
130+
var nullableDto = new NullableTimeOnlyDto { Time = time };
131+
json = nullableDto.ToJson();
132+
Assert.That(json, Is.EqualTo("{\"Time\":\"PT13H13M13S\"}"));
133+
var fromJsonNullableDto = json.FromJson<NullableTimeOnlyDto>();
134+
Assert.That(fromJsonNullableDto, Is.EqualTo(nullableDto));
135+
}
136+
137+
}
138+
#endif

tests/ServiceStack.Text.Tests/ServiceStack.Text.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@
2323
<ProjectReference Include="..\Northwind.Common\Northwind.Common.csproj" />
2424
</ItemGroup>
2525
<ItemGroup>
26-
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
27-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
28-
<PackageReference Include="NUnit" Version="3.10.1" />
26+
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
27+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
28+
<PackageReference Include="NUnit" Version="3.13.2" />
2929
<PackageReference Include="protobuf-net" Version="3.0.101" />
3030
<PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
3131
<PackageReference Include="System.Drawing.Primitives" Version="4.3.0" />

tests/ServiceStack.Text.TestsConsole/ServiceStack.Text.TestsConsole.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<OutputType>Exe</OutputType>
4-
<TargetFramework>net5.0</TargetFramework>
4+
<TargetFramework>net6.0</TargetFramework>
55
</PropertyGroup>
66
<ItemGroup>
77
<!-- <PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />-->

0 commit comments

Comments
 (0)