Skip to content

Commit c7ef724

Browse files
committed
Add built-in support for .NET 6 DateOnly and TimeOnly types
`DateOnly` requires 5 bytes (typically). `TimeOnly` requires 6 bytes for second resolution or 8 bytes for ticks resolution. This is automatically selected based on the original value. Closes MessagePack-CSharp#1240 # Conflicts: # src/MessagePack/net6.0/PublicAPI.Unshipped.txt
1 parent 50b549f commit c7ef724

File tree

6 files changed

+145
-18
lines changed

6 files changed

+145
-18
lines changed

README.md

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,24 +1499,36 @@ var resolver = MessagePack.Resolvers.CompositeResolver.Create(
14991499

15001500
## Reserved Extension Types
15011501

1502-
MessagePack for C# already used some MessagePack extension type codes, be careful to use same ext code.
1502+
MessagePack for C# already used some MessagePack extension type codes, be careful to avoid using the same ext code for other purposes.
1503+
1504+
Range | Reserved for
1505+
--|--
1506+
\[-128, -1\] | Reserved by the msgpack spec for predefined types
1507+
\[30, 120) | Reserved for this library's use to support common types in .NET
1508+
1509+
This leaves the following ranges for your use:
1510+
1511+
- \[0, 30)
1512+
- \[120, 127]
1513+
1514+
Within the *reserved* ranges, this library defines or implements extensions that use these type codes:
15031515

15041516
| Code | Type | Use by |
1505-
| --- | --- | --- |
1506-
| -1 | DateTime | MessagePack-spec reserved for timestamp |
1507-
| 30 | Vector2[] | for Unity, UnsafeBlitFormatter |
1508-
| 31 | Vector3[] | for Unity, UnsafeBlitFormatter |
1509-
| 32 | Vector4[] | for Unity, UnsafeBlitFormatter |
1510-
| 33 | Quaternion[] | for Unity, UnsafeBlitFormatter |
1511-
| 34 | Color[] | for Unity, UnsafeBlitFormatter |
1512-
| 35 | Bounds[] | for Unity, UnsafeBlitFormatter |
1513-
| 36 | Rect[] | for Unity, UnsafeBlitFormatter |
1514-
| 37 | Int[] | for Unity, UnsafeBlitFormatter |
1515-
| 38 | Float[] | for Unity, UnsafeBlitFormatter |
1516-
| 39 | Double[] | for Unity, UnsafeBlitFormatter |
1517-
| 98 | All | MessagePackCompression.Lz4BlockArray |
1518-
| 99 | All | MessagePackCompression.Lz4Block |
1519-
| 100 | object | TypelessFormatter |
1517+
| ---- | ---- | --- |
1518+
| -1 | DateTime | MessagePack-spec reserved for timestamp |
1519+
| 30 | Vector2[] | for Unity, UnsafeBlitFormatter |
1520+
| 31 | Vector3[] | for Unity, UnsafeBlitFormatter |
1521+
| 32 | Vector4[] | for Unity, UnsafeBlitFormatter |
1522+
| 33 | Quaternion[] | for Unity, UnsafeBlitFormatter |
1523+
| 34 | Color[] | for Unity, UnsafeBlitFormatter |
1524+
| 35 | Bounds[] | for Unity, UnsafeBlitFormatter |
1525+
| 36 | Rect[] | for Unity, UnsafeBlitFormatter |
1526+
| 37 | Int[] | for Unity, UnsafeBlitFormatter |
1527+
| 38 | Float[] | for Unity, UnsafeBlitFormatter |
1528+
| 39 | Double[] | for Unity, UnsafeBlitFormatter |
1529+
| 98 | All | MessagePackCompression.Lz4BlockArray |
1530+
| 99 | All | MessagePackCompression.Lz4Block |
1531+
| 100 | object | TypelessFormatter |
15201532

15211533
## Unity support
15221534

src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/DateTimeFormatters.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,50 @@ public DateTime[] Deserialize(ref MessagePackReader reader, MessagePackSerialize
7070
return array;
7171
}
7272
}
73+
74+
#if NET6_0_OR_GREATER
75+
/// <summary>
76+
/// Serializes a <see cref="DateOnly"/> value as an ordinary <see cref="int"/> using the <see cref="DateOnly.DayNumber"/>.
77+
/// </summary>
78+
public sealed class DateOnlyFormatter : IMessagePackFormatter<DateOnly>
79+
{
80+
public static readonly DateOnlyFormatter Instance = new DateOnlyFormatter();
81+
82+
private DateOnlyFormatter()
83+
{
84+
}
85+
86+
public void Serialize(ref MessagePackWriter writer, DateOnly value, MessagePackSerializerOptions options)
87+
{
88+
writer.Write(value.DayNumber);
89+
}
90+
91+
public DateOnly Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
92+
{
93+
return DateOnly.FromDayNumber(reader.ReadInt32());
94+
}
95+
}
96+
97+
/// <summary>
98+
/// Serializes a <see cref="TimeOnly"/> value as an extension, recording either seconds or ticks depending on the resolution required.
99+
/// </summary>
100+
public sealed class TimeOnlyFormatter : IMessagePackFormatter<TimeOnly>
101+
{
102+
public static readonly TimeOnlyFormatter Instance = new TimeOnlyFormatter();
103+
104+
private TimeOnlyFormatter()
105+
{
106+
}
107+
108+
public void Serialize(ref MessagePackWriter writer, TimeOnly value, MessagePackSerializerOptions options)
109+
{
110+
writer.Write(value.Ticks);
111+
}
112+
113+
public TimeOnly Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
114+
{
115+
return new TimeOnly(reader.ReadInt64());
116+
}
117+
}
118+
#endif
73119
}

src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/BuiltinResolver.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@ internal static class BuiltinResolverGetFormatterHelper
6464
{ typeof(byte), ByteFormatter.Instance },
6565
{ typeof(sbyte), SByteFormatter.Instance },
6666
{ typeof(DateTime), DateTimeFormatter.Instance },
67+
#if NET6_0_OR_GREATER
68+
{ typeof(DateOnly), DateOnlyFormatter.Instance },
69+
{ typeof(TimeOnly), TimeOnlyFormatter.Instance },
70+
#endif
6771
{ typeof(char), CharFormatter.Instance },
6872

69-
// Nulllable Primitive
73+
// Nullable Primitive
7074
{ typeof(Int16?), NullableInt16Formatter.Instance },
7175
{ typeof(Int32?), NullableInt32Formatter.Instance },
7276
{ typeof(Int64?), NullableInt64Formatter.Instance },

src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using System;
5-
using System.Linq;
5+
using Nerdbank.Streams;
66
using Xunit;
77
using Xunit.Abstractions;
88

@@ -77,5 +77,60 @@ public void DeserializeByteArrayFromFixArray_Array32()
7777
byte[] byte_array = MessagePackSerializer.Deserialize<byte[]>(input);
7878
Assert.Equal(new byte[] { 1, 2, 3 }, byte_array);
7979
}
80+
81+
#if NET6_0_OR_GREATER
82+
[Fact]
83+
public void DateOnly()
84+
{
85+
var value = new DateOnly(2012, 3, 5);
86+
this.AssertRoundtrip(value);
87+
this.AssertRoundtrip<DateOnly?>(value);
88+
this.AssertRoundtrip(new[] { value });
89+
}
90+
91+
[Fact]
92+
public void TimeOnly()
93+
{
94+
TimeOnly lowRes = new TimeOnly(5, 4, 3);
95+
this.AssertRoundtrip(lowRes);
96+
this.AssertRoundtrip<TimeOnly?>(lowRes);
97+
this.AssertRoundtrip(new[] { lowRes });
98+
99+
TimeOnly mediumRes = new TimeOnly(5, 4, 3, 2);
100+
this.AssertRoundtrip(mediumRes);
101+
this.AssertRoundtrip<TimeOnly?>(mediumRes);
102+
this.AssertRoundtrip(new[] { mediumRes });
103+
104+
TimeOnly highRes = new TimeOnly(lowRes.Ticks + 1);
105+
this.AssertRoundtrip(highRes);
106+
this.AssertRoundtrip(System.TimeOnly.MaxValue);
107+
}
108+
#endif
109+
110+
private void AssertRoundtrip<T>(T value)
111+
{
112+
Assert.Equal(value, this.Roundtrip(value, breakupBuffer: false));
113+
Assert.Equal(value, this.Roundtrip(value, breakupBuffer: true));
114+
}
115+
116+
private T Roundtrip<T>(T value, bool breakupBuffer = false)
117+
{
118+
byte[] msgpack = MessagePackSerializer.Serialize(value, MessagePackSerializerOptions.Standard);
119+
this.logger.WriteLine("{0} 0x{1}", value, TestUtilities.ToHex(msgpack));
120+
121+
if (breakupBuffer)
122+
{
123+
using (Sequence<byte> seq = new Sequence<byte>())
124+
{
125+
seq.Append(msgpack.AsMemory(0, msgpack.Length - 1));
126+
seq.Append(msgpack.AsMemory(msgpack.Length - 1, 1));
127+
return MessagePackSerializer.Deserialize<T>(seq, MessagePackSerializerOptions.Standard);
128+
}
129+
}
130+
else
131+
{
132+
return MessagePackSerializer.Deserialize<T>(msgpack, MessagePackSerializerOptions.Standard);
133+
}
134+
}
80135
}
81136
}

src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/TestUtilities.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ internal static class TestUtilities
1515
/// Gets a value indicating whether the mono runtime is executing this code.
1616
/// </summary>
1717
internal static bool IsRunningOnMono => Type.GetType("Mono.Runtime") != null;
18+
19+
internal static string ToHex(byte[] buffer) => BitConverter.ToString(buffer).Replace("-", string.Empty).ToLowerInvariant();
1820
}
1921

2022
public class NullTestOutputHelper : ITestOutputHelper
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
1+
MessagePack.Formatters.DateOnlyFormatter
2+
MessagePack.Formatters.DateOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> System.DateOnly
3+
MessagePack.Formatters.DateOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.DateOnly value, MessagePack.MessagePackSerializerOptions options) -> void
14
MessagePack.Formatters.StringInterningFormatter
25
MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> string
36
MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string value, MessagePack.MessagePackSerializerOptions options) -> void
47
MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void
8+
MessagePack.Formatters.TimeOnlyFormatter
9+
MessagePack.Formatters.TimeOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> System.TimeOnly
10+
MessagePack.Formatters.TimeOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.TimeOnly value, MessagePack.MessagePackSerializerOptions options) -> void
511
MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int
612
MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions
13+
static readonly MessagePack.Formatters.DateOnlyFormatter.Instance -> MessagePack.Formatters.DateOnlyFormatter
14+
static readonly MessagePack.Formatters.TimeOnlyFormatter.Instance -> MessagePack.Formatters.TimeOnlyFormatter

0 commit comments

Comments
 (0)