diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 15fd094b975..eb3550d0869 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -9,7 +9,7 @@
]
},
"boost.tool": {
- "version": "0.2.2",
+ "version": "0.2.6",
"commands": [
"boo"
]
diff --git a/src/HotChocolate/Core/HotChocolate.Core.sln b/src/HotChocolate/Core/HotChocolate.Core.sln
index e066b6c4233..a0a8a25218c 100644
--- a/src/HotChocolate/Core/HotChocolate.Core.sln
+++ b/src/HotChocolate/Core/HotChocolate.Core.sln
@@ -61,35 +61,39 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Execution.Benc
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Fetching", "src\Fetching\HotChocolate.Fetching.csproj", "{3ED68361-0D3D-45F5-8A83-444DD3D5C1BF}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Subscriptions.Redis.Tests", "test\Subscriptions.Redis.Tests\HotChocolate.Subscriptions.Redis.Tests.csproj", "{14648C7D-8958-4C94-B837-7F90F2F10081}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Subscriptions.Redis.Tests", "test\Subscriptions.Redis.Tests\HotChocolate.Subscriptions.Redis.Tests.csproj", "{14648C7D-8958-4C94-B837-7F90F2F10081}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Subscriptions.InMemory.Tests", "test\Subscriptions.InMemory.Tests\HotChocolate.Subscriptions.InMemory.Tests.csproj", "{F72E72DE-F163-4BCC-A442-94DB331D3C35}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Subscriptions.InMemory.Tests", "test\Subscriptions.InMemory.Tests\HotChocolate.Subscriptions.InMemory.Tests.csproj", "{F72E72DE-F163-4BCC-A442-94DB331D3C35}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Fetching.Tests", "test\Fetching.Tests\HotChocolate.Fetching.Tests.csproj", "{2A319702-6C2E-4184-8E71-43F87745B38D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Fetching.Tests", "test\Fetching.Tests\HotChocolate.Fetching.Tests.csproj", "{2A319702-6C2E-4184-8E71-43F87745B38D}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.StarWars", "benchmark\StarWars\HotChocolate.StarWars.csproj", "{BB53E6F4-2CA0-494C-85FD-DF22530476C0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.StarWars", "benchmark\StarWars\HotChocolate.StarWars.csproj", "{BB53E6F4-2CA0-494C-85FD-DF22530476C0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.CursorPagination", "src\Types.CursorPagination\HotChocolate.Types.CursorPagination.csproj", "{91F00C73-78E5-4400-8F9F-910BE99EDFD5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.CursorPagination", "src\Types.CursorPagination\HotChocolate.Types.CursorPagination.csproj", "{91F00C73-78E5-4400-8F9F-910BE99EDFD5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.OffsetPagination", "src\Types.OffsetPagination\HotChocolate.Types.OffsetPagination.csproj", "{738F2483-286E-4517-9660-5BFEEF343595}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.OffsetPagination", "src\Types.OffsetPagination\HotChocolate.Types.OffsetPagination.csproj", "{738F2483-286E-4517-9660-5BFEEF343595}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.CursorPagination.Tests", "test\Types.CursorPagination.Tests\HotChocolate.Types.CursorPagination.Tests.csproj", "{31AF3FED-A6C5-4DD5-99DF-03D4B4186D39}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.CursorPagination.Tests", "test\Types.CursorPagination.Tests\HotChocolate.Types.CursorPagination.Tests.csproj", "{31AF3FED-A6C5-4DD5-99DF-03D4B4186D39}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.OffsetPagination.Tests", "test\Types.OffsetPagination.Tests\HotChocolate.Types.OffsetPagination.Tests.csproj", "{9EF119AD-4168-49F8-B2B2-8F6F1B5E47C8}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.OffsetPagination.Tests", "test\Types.OffsetPagination.Tests\HotChocolate.Types.OffsetPagination.Tests.csproj", "{9EF119AD-4168-49F8-B2B2-8F6F1B5E47C8}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Records.Tests", "test\Types.Records.Tests\HotChocolate.Types.Records.Tests.csproj", "{CF2FA420-6295-4F83-8F87-340D8365282D}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Records.Tests", "test\Types.Records.Tests\HotChocolate.Types.Records.Tests.csproj", "{CF2FA420-6295-4F83-8F87-340D8365282D}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmark10", "benchmark10", "{4882F732-028B-48E9-99D6-E94E56B6406E}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Execution.Benchmarks", "benchmark10\Execution.Benchmarks\HotChocolate.Execution.Benchmarks.csproj", "{CBA38330-E94C-472F-8FFE-DA70DF0368EC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Execution.Benchmarks", "benchmark10\Execution.Benchmarks\HotChocolate.Execution.Benchmarks.csproj", "{CBA38330-E94C-472F-8FFE-DA70DF0368EC}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.StarWars", "benchmark10\StarWars\HotChocolate.StarWars.csproj", "{0BE70B30-33CC-4BB7-8EEA-4981A2E9417C}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.StarWars", "benchmark10\StarWars\HotChocolate.StarWars.csproj", "{0BE70B30-33CC-4BB7-8EEA-4981A2E9417C}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Scalars", "src\Types.Scalars\HotChocolate.Types.Scalars.csproj", "{8B7EEF6D-DFF2-4D7B-9E5D-6CA34A8BC260}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Scalars", "src\Types.Scalars\HotChocolate.Types.Scalars.csproj", "{8B7EEF6D-DFF2-4D7B-9E5D-6CA34A8BC260}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Scalars.Tests", "test\Types.Scalars.Tests\HotChocolate.Types.Scalars.Tests.csproj", "{480E09E8-A0C4-49A7-AAC4-4AA5EC0733C5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Scalars.Tests", "test\Types.Scalars.Tests\HotChocolate.Types.Scalars.Tests.csproj", "{480E09E8-A0C4-49A7-AAC4-4AA5EC0733C5}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HotChocolate.Types.Scalars.Upload", "src\Types.Scalars.Upload\HotChocolate.Types.Scalars.Upload.csproj", "{670D3B20-401F-4004-AD0B-865560D02CC3}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.Scalars.Upload", "src\Types.Scalars.Upload\HotChocolate.Types.Scalars.Upload.csproj", "{670D3B20-401F-4004-AD0B-865560D02CC3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.NodaTime", "src\Types.NodaTime\HotChocolate.Types.NodaTime.csproj", "{07F3E312-EC4C-4B71-A095-E478DF4CB52B}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HotChocolate.Types.NodaTime.Tests", "test\Types.NodaTime.Tests\HotChocolate.Types.NodaTime.Tests.csproj", "{414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -569,6 +573,30 @@ Global
{670D3B20-401F-4004-AD0B-865560D02CC3}.Release|x64.Build.0 = Release|Any CPU
{670D3B20-401F-4004-AD0B-865560D02CC3}.Release|x86.ActiveCfg = Release|Any CPU
{670D3B20-401F-4004-AD0B-865560D02CC3}.Release|x86.Build.0 = Release|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x64.Build.0 = Debug|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Debug|x86.Build.0 = Debug|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x64.ActiveCfg = Release|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x64.Build.0 = Release|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x86.ActiveCfg = Release|Any CPU
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B}.Release|x86.Build.0 = Release|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x64.Build.0 = Debug|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Debug|x86.Build.0 = Debug|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x64.ActiveCfg = Release|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x64.Build.0 = Release|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x86.ActiveCfg = Release|Any CPU
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -613,6 +641,8 @@ Global
{8B7EEF6D-DFF2-4D7B-9E5D-6CA34A8BC260} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
{480E09E8-A0C4-49A7-AAC4-4AA5EC0733C5} = {7462D089-D350-44D6-8131-896D949A65B7}
{670D3B20-401F-4004-AD0B-865560D02CC3} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
+ {07F3E312-EC4C-4B71-A095-E478DF4CB52B} = {37B9D3B1-CA34-4720-9A0B-CFF1E64F52C2}
+ {414B6DBE-1A3A-42DC-9116-3F9A433C6BBB} = {7462D089-D350-44D6-8131-896D949A65B7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E4D94C77-6657-4630-9D42-0A9AC5153A1B}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/IntToStructBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/IntToStructBaseType.cs
new file mode 100644
index 00000000000..e01992e3504
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/IntToStructBaseType.cs
@@ -0,0 +1,154 @@
+using HotChocolate.Language;
+using System.Diagnostics.CodeAnalysis;
+using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// This base class provides serialization functionality for noda time scalars
+ /// that have a result value and a struct runtime value.
+ ///
+ ///
+ /// The runtime type.
+ ///
+ public abstract class IntToStructBaseType
+ : ScalarType
+ where TRuntimeType : struct
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ ///
+ /// The name of the scalar.
+ ///
+ protected IntToStructBaseType(string name)
+ : base(name, BindingBehavior.Implicit)
+ {
+ }
+
+ ///
+ protected override TRuntimeType ParseLiteral(IntValueNode literal)
+ {
+ if (TryDeserialize(literal.ToInt32(), out TRuntimeType? value))
+ {
+ return value.Value;
+ }
+
+ throw new SerializationException(
+ string.Format(IntToStructBaseType_ParseLiteral_UnableToDeserializeInt, Name),
+ this);
+ }
+
+ ///
+ protected override IntValueNode ParseValue(TRuntimeType value)
+ {
+ if (TrySerialize(value, out var val))
+ {
+ return new IntValueNode(val.Value);
+ }
+
+ throw new SerializationException(
+ string.Format(IntToStructBaseType_ParseLiteral_UnableToDeserializeInt, Name),
+ this);
+ }
+
+ ///
+ public override IValueNode ParseResult(object? resultValue)
+ {
+ if (resultValue is null)
+ {
+ return NullValueNode.Default;
+ }
+
+ if (resultValue is int s)
+ {
+ return new IntValueNode(s);
+ }
+
+ if (resultValue is TRuntimeType v)
+ {
+ return ParseValue(v);
+ }
+
+ throw new SerializationException(
+ string.Format(IntToStructBaseType_ParseLiteral_UnableToDeserializeInt, Name),
+ this);
+ }
+
+ ///
+ public override bool TrySerialize(
+ object? runtimeValue,
+ out object? resultValue)
+ {
+ if (runtimeValue is null)
+ {
+ resultValue = null;
+ return true;
+ }
+
+ if (runtimeValue is TRuntimeType dt && TrySerialize(dt, out var val))
+ {
+ resultValue = val.Value;
+ return true;
+ }
+
+ resultValue = null;
+ return false;
+ }
+
+ ///
+ /// Tries to serialize the .net runtime representation to the
+ /// serialized result representation.
+ ///
+ ///
+ /// The .net runtime representation.
+ ///
+ ///
+ /// The serialized result value.
+ ///
+ ///
+ /// Returns the serialized result value.
+ ///
+ protected abstract bool TrySerialize(
+ TRuntimeType runtimeValue,
+ [NotNullWhen(true)] out int? resultValue);
+
+ ///
+ public override bool TryDeserialize(
+ object? resultValue,
+ out object? runtimeValue)
+ {
+ if (resultValue is null)
+ {
+ runtimeValue = null;
+ return true;
+ }
+
+ if (resultValue is int i && TryDeserialize(i, out TRuntimeType? val))
+ {
+ runtimeValue = val;
+ return true;
+ }
+
+ runtimeValue = null;
+ return false;
+ }
+
+ ///
+ /// Tries to deserializes the value from the output format to the .net
+ /// runtime representation.
+ ///
+ ///
+ /// The serialized result value.
+ ///
+ ///
+ /// The .net runtime representation.
+ ///
+ ///
+ /// true if the serialized value was correctly deserialized; otherwise, false.
+ ///
+ protected abstract bool TryDeserialize(
+ int resultValue,
+ [NotNullWhen(true)] out TRuntimeType? runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToClassBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToClassBaseType.cs
new file mode 100644
index 00000000000..118a19b17e7
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToClassBaseType.cs
@@ -0,0 +1,135 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Language;
+using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// This base class provides serialization functionality for noda time scalars
+ /// that have a result value and a class runtime value.
+ ///
+ ///
+ /// The runtime type.
+ ///
+ public abstract class StringToClassBaseType
+ : ScalarType
+ where TRuntimeType : class
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ ///
+ /// The name of the scalar.
+ ///
+ public StringToClassBaseType(string name)
+ : base(name, BindingBehavior.Implicit)
+ {
+ }
+
+ ///
+ protected override TRuntimeType ParseLiteral(StringValueNode literal)
+ {
+ if (TryDeserialize(literal.Value, out TRuntimeType? value))
+ {
+ return value;
+ }
+
+ throw new SerializationException(
+ string.Format(StringToClassBaseType_ParseLiteral_UnableToDeserializeString, Name),
+ this);
+ }
+
+ ///
+ protected override StringValueNode ParseValue(TRuntimeType value) =>
+ new(Serialize(value));
+
+ ///
+ public override IValueNode ParseResult(object? resultValue)
+ {
+ if (resultValue is null)
+ {
+ return NullValueNode.Default;
+ }
+
+ if (resultValue is string s)
+ {
+ return new StringValueNode(s);
+ }
+
+ if (resultValue is TRuntimeType v)
+ {
+ return ParseValue(v);
+ }
+
+ throw new SerializationException(
+ string.Format(StringToClassBaseType_ParseLiteral_UnableToDeserializeString, Name),
+ this);
+ }
+
+ ///
+ public override bool TrySerialize(object? runtimeValue, out object? resultValue)
+ {
+ if (runtimeValue is null)
+ {
+ resultValue = null;
+ return true;
+ }
+
+ if (runtimeValue is TRuntimeType dt)
+ {
+ resultValue = Serialize(dt);
+ return true;
+ }
+
+ resultValue = null;
+ return false;
+ }
+
+ ///
+ /// Serializes the .net runtime representation to the serialized result representation.
+ ///
+ ///
+ /// The .net value representation.
+ ///
+ ///
+ /// Returns the serialized result value.
+ ///
+ protected abstract string Serialize(TRuntimeType runtimeValue);
+
+ ///
+ public override bool TryDeserialize(object? resultValue, out object? runtimeValue)
+ {
+ if (resultValue is null)
+ {
+ runtimeValue = null;
+ return true;
+ }
+
+ if (resultValue is string s && TryDeserialize(s, out TRuntimeType? val))
+ {
+ runtimeValue = val;
+ return true;
+ }
+
+ runtimeValue = null;
+ return false;
+ }
+
+ //
+ /// Tries to deserializes the value from the output format to the .net
+ /// runtime representation.
+ ///
+ ///
+ /// The serialized result value.
+ ///
+ ///
+ /// The .net runtime representation.
+ ///
+ ///
+ /// true if the serialized value was correctly deserialized; otherwise, false.
+ ///
+ protected abstract bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out TRuntimeType? runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs
new file mode 100644
index 00000000000..4644b37e6a1
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/BaseTypes/StringToStructBaseType.cs
@@ -0,0 +1,137 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Language;
+using static HotChocolate.Types.NodaTime.Properties.NodaTimeResources;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// This base class provides serialization functionality for noda time scalars
+ /// that have a result value and a struct runtime value.
+ ///
+ ///
+ /// The runtime type.
+ ///
+ public abstract class StringToStructBaseType
+ : ScalarType
+ where TRuntimeType : struct
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ ///
+ /// The name of the scalar.
+ ///
+ public StringToStructBaseType(string name)
+ : base(name, BindingBehavior.Implicit)
+ {
+ }
+
+ ///
+ protected override TRuntimeType ParseLiteral(StringValueNode literal)
+ {
+ if (TryDeserialize(literal.Value, out TRuntimeType? value))
+ {
+ return value.Value;
+ }
+
+ throw new SerializationException(
+ string.Format(StringToStructBaseType_ParseLiteral_UnableToDeserializeString, Name),
+ this);
+ }
+
+ ///
+ protected override StringValueNode ParseValue(TRuntimeType value)
+ {
+ return new(Serialize(value));
+ }
+
+ ///
+ public override IValueNode ParseResult(object? resultValue)
+ {
+ if (resultValue is null)
+ {
+ return NullValueNode.Default;
+ }
+
+ if (resultValue is string s)
+ {
+ return new StringValueNode(s);
+ }
+
+ if (resultValue is TRuntimeType v)
+ {
+ return ParseValue(v);
+ }
+
+ throw new SerializationException(
+ string.Format(StringToStructBaseType_ParseLiteral_UnableToDeserializeString, Name),
+ this);
+ }
+
+ ///
+ public override bool TrySerialize(object? runtimeValue, out object? resultValue)
+ {
+ if (runtimeValue is null)
+ {
+ resultValue = null;
+ return true;
+ }
+
+ if (runtimeValue is TRuntimeType dt)
+ {
+ resultValue = Serialize(dt);
+ return true;
+ }
+
+ resultValue = null;
+ return false;
+ }
+
+ ///
+ /// Serializes the .net runtime representation to the serialized result representation.
+ ///
+ ///
+ /// The .net value representation.
+ ///
+ ///
+ /// Returns the serialized result value.
+ ///
+ protected abstract string Serialize(TRuntimeType runtimeValue);
+
+ ///
+ public override bool TryDeserialize(object? resultValue, out object? runtimeValue)
+ {
+ if (resultValue is null)
+ {
+ runtimeValue = null;
+ return true;
+ }
+
+ if (resultValue is string s && TryDeserialize(s, out TRuntimeType? val))
+ {
+ runtimeValue = val;
+ return true;
+ }
+
+ runtimeValue = null;
+ return false;
+ }
+
+ ///
+ /// Tries to deserializes the value from the output format to the .net
+ /// runtime representation.
+ ///
+ ///
+ /// The serialized result value.
+ ///
+ ///
+ /// The .net runtime representation.
+ ///
+ ///
+ /// true if the serialized value was correctly deserialized; otherwise, false.
+ ///
+ protected abstract bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out TRuntimeType? runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/DateTimeZoneType.cs b/src/HotChocolate/Core/src/Types.NodaTime/DateTimeZoneType.cs
new file mode 100644
index 00000000000..6476fa24713
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/DateTimeZoneType.cs
@@ -0,0 +1,43 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// Represents a time zone - a mapping between UTC and local time.
+ /// A time zone maps UTC instants to local times - or, equivalently,
+ /// to the offset from UTC at any particular instant.
+ ///
+ public class DateTimeZoneType : StringToClassBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public DateTimeZoneType() : base("DateTimeZone")
+ {
+ Description = NodaTimeResources.DateTimeZoneType_Description;
+ }
+
+ ///
+ protected override string Serialize(DateTimeZone runtimeValue)
+ => runtimeValue.Id;
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out DateTimeZone? runtimeValue)
+ {
+ DateTimeZone? result = DateTimeZoneProviders.Tzdb.GetZoneOrNull(resultValue);
+
+ if (result == null)
+ {
+ runtimeValue = null;
+ return false;
+ }
+
+ runtimeValue = result;
+ return true;
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs
new file mode 100644
index 00000000000..f8ff33b95ad
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/DurationType.cs
@@ -0,0 +1,36 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// Represents a fixed (and calendar-independent) length of time.
+ ///
+ public class DurationType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public DurationType() : base("Duration")
+ {
+ Description = NodaTimeResources.DurationType_Description;
+ }
+
+ ///
+ protected override string Serialize(Duration runtimeValue)
+ => DurationPattern.Roundtrip
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out Duration? runtimeValue)
+ => DurationPattern.Roundtrip
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs b/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs
new file mode 100644
index 00000000000..3ef5df23f2f
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/Extensions/PatternExtensions.cs
@@ -0,0 +1,43 @@
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ internal static class PatternExtensions
+ {
+ public static bool TryParse(
+ this IPattern pattern,
+ string text,
+ out NodaTimeType? output)
+ where NodaTimeType : struct
+ {
+ ParseResult result = pattern.Parse(text);
+
+ if (result.Success)
+ {
+ output = result.Value;
+ return true;
+ }
+
+ output = null;
+ return false;
+ }
+
+ public static bool TryParse(
+ this IPattern pattern,
+ string text,
+ out NodaTimeType? output)
+ where NodaTimeType : class
+ {
+ ParseResult result = pattern.Parse(text);
+
+ if (result.Success)
+ {
+ output = result.Value;
+ return true;
+ }
+
+ output = null;
+ return false;
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/HotChocolate.Types.NodaTime.csproj b/src/HotChocolate/Core/src/Types.NodaTime/HotChocolate.Types.NodaTime.csproj
new file mode 100644
index 00000000000..b0ea3c73010
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/HotChocolate.Types.NodaTime.csproj
@@ -0,0 +1,56 @@
+
+
+
+ enable
+ $(WarningsAsErrors);nullable
+ true
+
+
+
+ HotChocolate.Types.NodaTime
+ HotChocolate.Types.NodaTime
+ HotChocolate.Types.NodaTime
+ Adds additional scalars that map to NodaTime runtime types
+
+
+
+
+
+
+
+
+
+
+
+
+ ResXFileCodeGenerator
+ NodaTimeResources.Designer.cs
+
+
+
+
+
+ True
+ True
+ NodaTimeResources.resx
+
+
+
+
+
+
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs
new file mode 100644
index 00000000000..88759e3ae74
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/InstantType.cs
@@ -0,0 +1,36 @@
+using System.Globalization;
+using NodaTime;
+using NodaTime.Text;
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Types.NodaTime.Properties;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// Represents an instant on the global timeline, with nanosecond resolution.
+ ///
+ public class InstantType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public InstantType() : base("Instant")
+ {
+ Description = NodaTimeResources.InstantType_Description;
+ }
+
+ ///
+ protected override string Serialize(Instant runtimeValue)
+ => InstantPattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out Instant? runtimeValue)
+ => InstantPattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/IsoDayOfWeekType.cs b/src/HotChocolate/Core/src/Types.NodaTime/IsoDayOfWeekType.cs
new file mode 100644
index 00000000000..056047f260b
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/IsoDayOfWeekType.cs
@@ -0,0 +1,51 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// Equates the days of the week with their numerical value according to ISO-8601.
+ /// Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7.
+ ///
+ public class IsoDayOfWeekType : IntToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public IsoDayOfWeekType() : base("IsoDayOfWeek")
+ {
+ Description = NodaTimeResources.IsoDayOfWeekType_Description;
+ }
+
+ ///
+ protected override bool TrySerialize(
+ IsoDayOfWeek runtimeValue,
+ [NotNullWhen(true)] out int? resultValue)
+ {
+ if (runtimeValue == IsoDayOfWeek.None)
+ {
+ resultValue = null;
+ return false;
+ }
+
+ resultValue = (int)runtimeValue;
+ return true;
+ }
+
+ ///
+ protected override bool TryDeserialize(
+ int resultValue,
+ [NotNullWhen(true)] out IsoDayOfWeek? runtimeValue)
+ {
+ if (resultValue < 1 || resultValue > 7)
+ {
+ runtimeValue = null;
+ return false;
+ }
+
+ runtimeValue = (IsoDayOfWeek)resultValue;
+ return true;
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs
new file mode 100644
index 00000000000..f850b042cf8
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateTimeType.cs
@@ -0,0 +1,36 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// A date and time in a particular calendar system.
+ ///
+ public class LocalDateTimeType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public LocalDateTimeType() : base("LocalDateTime")
+ {
+ Description = NodaTimeResources.LocalDateTimeType_Description;
+ }
+
+ ///
+ protected override string Serialize(LocalDateTime runtimeValue)
+ => LocalDateTimePattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out LocalDateTime? runtimeValue)
+ => LocalDateTimePattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs
new file mode 100644
index 00000000000..852e892db2e
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalDateType.cs
@@ -0,0 +1,37 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// LocalDate is an immutable struct representing a date within the calendar,
+ /// with no reference to a particular time zone or time of day.
+ ///
+ public class LocalDateType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public LocalDateType() : base("LocalDate")
+ {
+ Description = NodaTimeResources.LocalDateType_Description;
+ }
+
+ ///
+ protected override string Serialize(LocalDate runtimeValue)
+ => LocalDatePattern.Iso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out LocalDate? runtimeValue)
+ => LocalDatePattern.Iso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs
new file mode 100644
index 00000000000..e59c145e231
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/LocalTimeType.cs
@@ -0,0 +1,37 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// LocalTime is an immutable struct representing a time of day,
+ /// with no reference to a particular calendar, time zone or date.
+ ///
+ public class LocalTimeType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public LocalTimeType() : base("LocalTime")
+ {
+ Description = NodaTimeResources.LocalTimeType_Description;
+ }
+
+ ///
+ protected override string Serialize(LocalTime runtimeValue)
+ => LocalTimePattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out LocalTime? runtimeValue)
+ => LocalTimePattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs
new file mode 100644
index 00000000000..a3cb247be8f
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateTimeType.cs
@@ -0,0 +1,36 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// A local date and time in a particular calendar system, combined with an offset from UTC.
+ ///
+ public class OffsetDateTimeType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public OffsetDateTimeType() : base("OffsetDateTime")
+ {
+ Description = NodaTimeResources.OffsetDateTimeType_Description;
+ }
+
+ ///
+ protected override string Serialize(OffsetDateTime runtimeValue)
+ => OffsetDateTimePattern.GeneralIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out OffsetDateTime? runtimeValue)
+ => OffsetDateTimePattern.ExtendedIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs
new file mode 100644
index 00000000000..d123d0df004
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetDateType.cs
@@ -0,0 +1,38 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// A combination of a LocalDate and an Offset,
+ /// to represent a date at a specific offset from UTC but
+ /// without any time-of-day information.
+ ///
+ public class OffsetDateType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public OffsetDateType() : base("OffsetDate")
+ {
+ Description = NodaTimeResources.OffsetDateType_Description;
+ }
+
+ ///
+ protected override string Serialize(OffsetDate runtimeValue)
+ => OffsetDatePattern.GeneralIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out OffsetDate? runtimeValue)
+ => OffsetDatePattern.GeneralIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs
new file mode 100644
index 00000000000..286b4140792
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetTimeType.cs
@@ -0,0 +1,37 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset
+ /// from UTC but without any date information.
+ ///
+ public class OffsetTimeType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public OffsetTimeType() : base("OffsetTime")
+ {
+ Description = NodaTimeResources.OffsetTimeType_Description;
+ }
+
+ ///
+ protected override string Serialize(OffsetTime runtimeValue)
+ => OffsetTimePattern.GeneralIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out OffsetTime? runtimeValue)
+ => OffsetTimePattern.GeneralIso
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs
new file mode 100644
index 00000000000..eaa74d4d95d
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/OffsetType.cs
@@ -0,0 +1,38 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// An offset from UTC in seconds.
+ /// A positive value means that the local time is ahead of UTC (e.g. for Europe);
+ /// a negative value means that the local time is behind UTC (e.g. for America).
+ ///
+ public class OffsetType : StringToStructBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public OffsetType() : base("Offset")
+ {
+ Description = NodaTimeResources.OffsetType_Description;
+ }
+
+ ///
+ protected override string Serialize(Offset runtimeValue)
+ => OffsetPattern.GeneralInvariantWithZ
+ .WithCulture(CultureInfo.InvariantCulture)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out Offset? runtimeValue)
+ => OffsetPattern.GeneralInvariantWithZ
+ .WithCulture(CultureInfo.InvariantCulture)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs b/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs
new file mode 100644
index 00000000000..64299c02f6c
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/PeriodType.cs
@@ -0,0 +1,32 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// Represents a period of time expressed in human chronological terms:
+ /// hours, days, weeks, months and so on.
+ ///
+ public class PeriodType : StringToClassBaseType
+ {
+ ///
+ /// Initializes a new instance of .
+ ///
+ public PeriodType() : base("Period")
+ {
+ Description = NodaTimeResources.PeriodType_Description;
+ }
+
+ ///
+ protected override string Serialize(Period runtimeValue)
+ => PeriodPattern.Roundtrip.Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out Period? runtimeValue)
+ => PeriodPattern.Roundtrip.TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs
new file mode 100644
index 00000000000..484ebfda87b
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.Designer.cs
@@ -0,0 +1,144 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace HotChocolate.Types.NodaTime.Properties {
+ using System;
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class NodaTimeResources {
+
+ private static System.Resources.ResourceManager resourceMan;
+
+ private static System.Globalization.CultureInfo resourceCulture;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal NodaTimeResources() {
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.Equals(null, resourceMan)) {
+ System.Resources.ResourceManager temp = new System.Resources.ResourceManager("HotChocolate.Types.NodaTime.Properties.NodaTimeResources", typeof(NodaTimeResources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ internal static string IntToStructBaseType_ParseLiteral_UnableToDeserializeInt {
+ get {
+ return ResourceManager.GetString("IntToStructBaseType_ParseLiteral_UnableToDeserializeInt", resourceCulture);
+ }
+ }
+
+ internal static string StringToClassBaseType_ParseLiteral_UnableToDeserializeString {
+ get {
+ return ResourceManager.GetString("StringToClassBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture);
+ }
+ }
+
+ internal static string StringToStructBaseType_ParseLiteral_UnableToDeserializeString {
+ get {
+ return ResourceManager.GetString("StringToStructBaseType_ParseLiteral_UnableToDeserializeString", resourceCulture);
+ }
+ }
+
+ internal static string DateTimeZoneType_Description {
+ get {
+ return ResourceManager.GetString("DateTimeZoneType_Description", resourceCulture);
+ }
+ }
+
+ internal static string DurationType_Description {
+ get {
+ return ResourceManager.GetString("DurationType_Description", resourceCulture);
+ }
+ }
+
+ internal static string InstantType_Description {
+ get {
+ return ResourceManager.GetString("InstantType_Description", resourceCulture);
+ }
+ }
+
+ internal static string IsoDayOfWeekType_Description {
+ get {
+ return ResourceManager.GetString("IsoDayOfWeekType_Description", resourceCulture);
+ }
+ }
+
+ internal static string LocalDateTimeType_Description {
+ get {
+ return ResourceManager.GetString("LocalDateTimeType_Description", resourceCulture);
+ }
+ }
+
+ internal static string LocalDateType_Description {
+ get {
+ return ResourceManager.GetString("LocalDateType_Description", resourceCulture);
+ }
+ }
+
+ internal static string LocalTimeType_Description {
+ get {
+ return ResourceManager.GetString("LocalTimeType_Description", resourceCulture);
+ }
+ }
+
+ internal static string OffsetDateTimeType_Description {
+ get {
+ return ResourceManager.GetString("OffsetDateTimeType_Description", resourceCulture);
+ }
+ }
+
+ internal static string OffsetDateType_Description {
+ get {
+ return ResourceManager.GetString("OffsetDateType_Description", resourceCulture);
+ }
+ }
+
+ internal static string OffsetTimeType_Description {
+ get {
+ return ResourceManager.GetString("OffsetTimeType_Description", resourceCulture);
+ }
+ }
+
+ internal static string OffsetType_Description {
+ get {
+ return ResourceManager.GetString("OffsetType_Description", resourceCulture);
+ }
+ }
+
+ internal static string PeriodType_Description {
+ get {
+ return ResourceManager.GetString("PeriodType_Description", resourceCulture);
+ }
+ }
+
+ internal static string ZonedDateTimeType_Description {
+ get {
+ return ResourceManager.GetString("ZonedDateTimeType_Description", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx
new file mode 100644
index 00000000000..5b3c434deef
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/Properties/NodaTimeResources.resx
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 1.3
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Unable to deserialize integer to {0}
+
+
+ Unable to deserialize string to {0}
+
+
+ Unable to deserialize string to {0}
+
+
+ Represents a time zone - a mapping between UTC and local time.
+A time zone maps UTC instants to local times - or, equivalently, to the offset from UTC at any particular instant.
+
+
+ Represents a fixed (and calendar-independent) length of time.
+
+
+ Represents an instant on the global timeline, with nanosecond resolution.
+
+
+ Equates the days of the week with their numerical value according to ISO-8601.
+ Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7.
+
+
+ A date and time in a particular calendar system.
+
+
+ LocalDate is an immutable struct representing a date within the calendar, with no reference to a particular time zone or time of day.
+
+
+ LocalTime is an immutable struct representing a time of day, with no reference to a particular calendar, time zone or date.
+
+
+ A local date and time in a particular calendar system, combined with an offset from UTC.
+
+
+ A combination of a LocalDate and an Offset, to represent a date at a specific offset from UTC but without any time-of-day information.
+
+
+ A combination of a LocalTime and an Offset, to represent a time-of-day at a specific offset from UTC but without any date information.
+
+
+ An offset from UTC in seconds.
+ A positive value means that the local time is ahead of UTC (e.g. for Europe); a negative value means that the local time is behind UTC (e.g. for America).
+
+
+ Represents a period of time expressed in human chronological terms: hours, days, weeks, months and so on.
+
+
+ A LocalDateTime in a specific time zone and with a particular offset to distinguish between otherwise-ambiguous instants.
+A ZonedDateTime is global, in that it maps to a single Instant.
+
+
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Shipped.txt b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Shipped.txt
new file mode 100644
index 00000000000..ab058de62d4
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Shipped.txt
@@ -0,0 +1 @@
+#nullable enable
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Unshipped.txt b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Unshipped.txt
new file mode 100644
index 00000000000..d54a80a9848
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/PublicAPI.Unshipped.txt
@@ -0,0 +1,79 @@
+abstract HotChocolate.Types.NodaTime.IntToStructBaseType.TryDeserialize(int resultValue, out TRuntimeType? runtimeValue) -> bool
+abstract HotChocolate.Types.NodaTime.IntToStructBaseType.TrySerialize(TRuntimeType runtimeValue, out int? resultValue) -> bool
+abstract HotChocolate.Types.NodaTime.StringToClassBaseType.Serialize(TRuntimeType runtimeValue) -> string
+abstract HotChocolate.Types.NodaTime.StringToClassBaseType.TryDeserialize(string resultValue, out TRuntimeType runtimeValue) -> bool
+abstract HotChocolate.Types.NodaTime.StringToStructBaseType.Serialize(TRuntimeType runtimeValue) -> string
+abstract HotChocolate.Types.NodaTime.StringToStructBaseType.TryDeserialize(string resultValue, out TRuntimeType? runtimeValue) -> bool
+HotChocolate.Types.NodaTime.DateTimeZoneType
+HotChocolate.Types.NodaTime.DateTimeZoneType.DateTimeZoneType() -> void
+HotChocolate.Types.NodaTime.DurationType
+HotChocolate.Types.NodaTime.DurationType.DurationType() -> void
+HotChocolate.Types.NodaTime.InstantType
+HotChocolate.Types.NodaTime.InstantType.InstantType() -> void
+HotChocolate.Types.NodaTime.IntToStructBaseType
+HotChocolate.Types.NodaTime.IntToStructBaseType.IntToStructBaseType(string name) -> void
+HotChocolate.Types.NodaTime.IsoDayOfWeekType
+HotChocolate.Types.NodaTime.IsoDayOfWeekType.IsoDayOfWeekType() -> void
+HotChocolate.Types.NodaTime.LocalDateTimeType
+HotChocolate.Types.NodaTime.LocalDateTimeType.LocalDateTimeType() -> void
+HotChocolate.Types.NodaTime.LocalDateType
+HotChocolate.Types.NodaTime.LocalDateType.LocalDateType() -> void
+HotChocolate.Types.NodaTime.LocalTimeType
+HotChocolate.Types.NodaTime.LocalTimeType.LocalTimeType() -> void
+HotChocolate.Types.NodaTime.OffsetDateTimeType
+HotChocolate.Types.NodaTime.OffsetDateTimeType.OffsetDateTimeType() -> void
+HotChocolate.Types.NodaTime.OffsetDateType
+HotChocolate.Types.NodaTime.OffsetDateType.OffsetDateType() -> void
+HotChocolate.Types.NodaTime.OffsetTimeType
+HotChocolate.Types.NodaTime.OffsetTimeType.OffsetTimeType() -> void
+HotChocolate.Types.NodaTime.OffsetType
+HotChocolate.Types.NodaTime.OffsetType.OffsetType() -> void
+HotChocolate.Types.NodaTime.PeriodType
+HotChocolate.Types.NodaTime.PeriodType.PeriodType() -> void
+HotChocolate.Types.NodaTime.StringToClassBaseType
+HotChocolate.Types.NodaTime.StringToClassBaseType.StringToClassBaseType(string name) -> void
+HotChocolate.Types.NodaTime.StringToStructBaseType
+HotChocolate.Types.NodaTime.StringToStructBaseType.StringToStructBaseType(string name) -> void
+HotChocolate.Types.NodaTime.ZonedDateTimeType
+HotChocolate.Types.NodaTime.ZonedDateTimeType.ZonedDateTimeType() -> void
+override HotChocolate.Types.NodaTime.DateTimeZoneType.Serialize(NodaTime.DateTimeZone runtimeValue) -> string
+override HotChocolate.Types.NodaTime.DateTimeZoneType.TryDeserialize(string resultValue, out NodaTime.DateTimeZone runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.DurationType.Serialize(NodaTime.Duration runtimeValue) -> string
+override HotChocolate.Types.NodaTime.DurationType.TryDeserialize(string resultValue, out NodaTime.Duration? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.InstantType.Serialize(NodaTime.Instant runtimeValue) -> string
+override HotChocolate.Types.NodaTime.InstantType.TryDeserialize(string resultValue, out NodaTime.Instant? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.IntToStructBaseType.ParseLiteral(HotChocolate.Language.IntValueNode literal) -> TRuntimeType
+override HotChocolate.Types.NodaTime.IntToStructBaseType.ParseResult(object resultValue) -> HotChocolate.Language.IValueNode
+override HotChocolate.Types.NodaTime.IntToStructBaseType.ParseValue(TRuntimeType value) -> HotChocolate.Language.IntValueNode
+override HotChocolate.Types.NodaTime.IntToStructBaseType.TryDeserialize(object resultValue, out object runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.IntToStructBaseType.TrySerialize(object runtimeValue, out object resultValue) -> bool
+override HotChocolate.Types.NodaTime.IsoDayOfWeekType.TryDeserialize(int resultValue, out NodaTime.IsoDayOfWeek? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.IsoDayOfWeekType.TrySerialize(NodaTime.IsoDayOfWeek runtimeValue, out int? resultValue) -> bool
+override HotChocolate.Types.NodaTime.LocalDateTimeType.Serialize(NodaTime.LocalDateTime runtimeValue) -> string
+override HotChocolate.Types.NodaTime.LocalDateTimeType.TryDeserialize(string resultValue, out NodaTime.LocalDateTime? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.LocalDateType.Serialize(NodaTime.LocalDate runtimeValue) -> string
+override HotChocolate.Types.NodaTime.LocalDateType.TryDeserialize(string resultValue, out NodaTime.LocalDate? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.LocalTimeType.Serialize(NodaTime.LocalTime runtimeValue) -> string
+override HotChocolate.Types.NodaTime.LocalTimeType.TryDeserialize(string resultValue, out NodaTime.LocalTime? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.OffsetDateTimeType.Serialize(NodaTime.OffsetDateTime runtimeValue) -> string
+override HotChocolate.Types.NodaTime.OffsetDateTimeType.TryDeserialize(string resultValue, out NodaTime.OffsetDateTime? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.OffsetDateType.Serialize(NodaTime.OffsetDate runtimeValue) -> string
+override HotChocolate.Types.NodaTime.OffsetDateType.TryDeserialize(string resultValue, out NodaTime.OffsetDate? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.OffsetTimeType.Serialize(NodaTime.OffsetTime runtimeValue) -> string
+override HotChocolate.Types.NodaTime.OffsetTimeType.TryDeserialize(string resultValue, out NodaTime.OffsetTime? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.OffsetType.Serialize(NodaTime.Offset runtimeValue) -> string
+override HotChocolate.Types.NodaTime.OffsetType.TryDeserialize(string resultValue, out NodaTime.Offset? runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.PeriodType.Serialize(NodaTime.Period runtimeValue) -> string
+override HotChocolate.Types.NodaTime.PeriodType.TryDeserialize(string resultValue, out NodaTime.Period runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.StringToClassBaseType.ParseLiteral(HotChocolate.Language.StringValueNode literal) -> TRuntimeType
+override HotChocolate.Types.NodaTime.StringToClassBaseType.ParseResult(object resultValue) -> HotChocolate.Language.IValueNode
+override HotChocolate.Types.NodaTime.StringToClassBaseType.ParseValue(TRuntimeType value) -> HotChocolate.Language.StringValueNode
+override HotChocolate.Types.NodaTime.StringToClassBaseType.TryDeserialize(object resultValue, out object runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.StringToClassBaseType.TrySerialize(object runtimeValue, out object resultValue) -> bool
+override HotChocolate.Types.NodaTime.StringToStructBaseType.ParseLiteral(HotChocolate.Language.StringValueNode literal) -> TRuntimeType
+override HotChocolate.Types.NodaTime.StringToStructBaseType.ParseResult(object resultValue) -> HotChocolate.Language.IValueNode
+override HotChocolate.Types.NodaTime.StringToStructBaseType.ParseValue(TRuntimeType value) -> HotChocolate.Language.StringValueNode
+override HotChocolate.Types.NodaTime.StringToStructBaseType.TryDeserialize(object resultValue, out object runtimeValue) -> bool
+override HotChocolate.Types.NodaTime.StringToStructBaseType.TrySerialize(object runtimeValue, out object resultValue) -> bool
+override HotChocolate.Types.NodaTime.ZonedDateTimeType.Serialize(NodaTime.ZonedDateTime runtimeValue) -> string
+override HotChocolate.Types.NodaTime.ZonedDateTimeType.TryDeserialize(string resultValue, out NodaTime.ZonedDateTime? runtimeValue) -> bool
\ No newline at end of file
diff --git a/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs
new file mode 100644
index 00000000000..5c36838e779
--- /dev/null
+++ b/src/HotChocolate/Core/src/Types.NodaTime/ZonedDateTimeType.cs
@@ -0,0 +1,40 @@
+using System.Diagnostics.CodeAnalysis;
+using HotChocolate.Types.NodaTime.Properties;
+using NodaTime;
+using NodaTime.Text;
+
+namespace HotChocolate.Types.NodaTime
+{
+ ///
+ /// A LocalDateTime in a specific time zone and with a particular offset to distinguish between
+ /// otherwise-ambiguous instants.\nA ZonedDateTime is global, in that it maps to
+ /// a single Instant.
+ ///
+ public class ZonedDateTimeType : StringToStructBaseType
+ {
+ private static readonly string formatString = "uuuu'-'MM'-'dd'T'HH':'mm':'ss' 'z' 'o";
+
+ ///
+ /// Initializes a new instance of .
+ ///
+ public ZonedDateTimeType()
+ : base("ZonedDateTime")
+ {
+ Description = NodaTimeResources.ZonedDateTimeType_Description;
+ }
+
+ ///
+ protected override string Serialize(ZonedDateTime runtimeValue)
+ => ZonedDateTimePattern
+ .CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb)
+ .Format(runtimeValue);
+
+ ///
+ protected override bool TryDeserialize(
+ string resultValue,
+ [NotNullWhen(true)] out ZonedDateTime? runtimeValue)
+ => ZonedDateTimePattern
+ .CreateWithInvariantCulture(formatString, DateTimeZoneProviders.Tzdb)
+ .TryParse(resultValue, out runtimeValue);
+ }
+}
diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs
index 54f4c98e18d..9ffbdc7de0c 100644
--- a/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Scalars/ScalarType~1.cs
@@ -9,8 +9,7 @@ namespace HotChocolate.Types
/// GraphQL responses take the form of a hierarchical tree;
/// the leaves on these trees are GraphQL scalars.
///
- public abstract class ScalarType
- : ScalarType
+ public abstract class ScalarType : ScalarType
{
///
protected ScalarType(NameString name, BindingBehavior bind = BindingBehavior.Explicit)
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs
new file mode 100644
index 00000000000..5a863c27cbe
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DateTimeZoneTypeTests.cs
@@ -0,0 +1,114 @@
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class DateTimeZoneTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public DateTimeZone Utc => DateTimeZone.Utc;
+ public DateTimeZone Rome => DateTimeZoneProviders.Tzdb["Europe/Rome"];
+ public DateTimeZone Chihuahua => DateTimeZoneProviders.Tzdb["America/Chihuahua"];
+ }
+
+ public class Mutation
+ {
+ public string Test(DateTimeZone arg)
+ {
+ return arg.Id;
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public DateTimeZoneTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturnsUtc()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: utc }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("UTC", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsRome()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: rome }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("Europe/Rome", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsChihuahua()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: chihuahua }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("America/Chihuahua", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: DateTimeZone!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "Europe/Amsterdam")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("Europe/Amsterdam", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: DateTimeZone!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "Europe/Hamster")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"Europe/Amsterdam\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("Europe/Amsterdam", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"Europe/Hamster\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to DateTimeZone", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs
new file mode 100644
index 00000000000..2846785ce1c
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/DurationTypeTests.cs
@@ -0,0 +1,232 @@
+using System;
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class DurationTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public Duration PositiveWithDecimals => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19));
+ public Duration NegativeWithDecimals => -Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10, 19));
+ public Duration PositiveWithoutDecimals => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 10));
+ public Duration PositiveWithoutSeconds => Duration.FromTimeSpan(new TimeSpan(123, 7, 53, 0));
+ public Duration PositiveWithoutMinutes => Duration.FromTimeSpan(new TimeSpan(123, 7, 0, 0));
+ public Duration PositiveWithRoundtrip => Duration.FromTimeSpan(new TimeSpan(123, 26, 0, 70));
+ }
+
+ public class Mutation
+ {
+ public Duration Test(Duration arg)
+ => arg + Duration.FromMinutes(10);
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public DurationTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturnsSerializedDataWithDecimals()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: positiveWithDecimals }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("123:07:53:10.019", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsSerializedDataWithNegativeValue()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: negativeWithDecimals }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("-123:07:53:10.019", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsSerializedDataWithoutDecimals()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutDecimals }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("123:07:53:10", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsSerializedDataWithoutSeconds()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutSeconds }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("123:07:53:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsSerializedDataWithoutMinutes()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: positiveWithoutMinutes }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("123:07:00:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsSerializedDataWithRoundtrip()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: positiveWithRoundtrip }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("124:02:01:10", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesInputWithDecimals()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "09:22:01:00.019")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("9:22:11:00.019", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesInputWithoutDecimals()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "09:22:01:00")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("9:22:11:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesInputWithoutLeadingZero()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "9:22:01:00")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("9:22:11:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesInputWithNegativeValue()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "-9:22:01:00")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("-9:21:51:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationDoesntParseInputWithPlusSign()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "+09:22:01:00")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void MutationDoesntParseInputWithOverflownHours()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Duration!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "9:26:01:00")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void MutationParsesLiteralWithDecimals()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"09:22:01:00.019\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("9:22:11:00.019", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesLiteralWithoutDecimals()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"09:22:01:00\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("9:22:11:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesLiteralWithoutLeadingZero()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"09:22:01:00\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("9:22:11:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesLiteralWithNegativeValue()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"-9:22:01:00\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("-9:21:51:00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationDoesntParseLiteralWithPlusSign()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"+09:22:01:00\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void MutationDoesntParseLiteralWithOverflownHours()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"9:26:01:00\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj
new file mode 100644
index 00000000000..ba278a8d870
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/HotChocolate.Types.NodaTime.Tests.csproj
@@ -0,0 +1,28 @@
+
+
+
+ HotChocolate.Types.NodaTime.Tests
+ HotChocolate.Types.NodaTime
+ enable
+ $(WarningsAsErrors);nullable
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs
new file mode 100644
index 00000000000..f64c56a9b55
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/InstantTypeTests.cs
@@ -0,0 +1,96 @@
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class InstantTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public Instant One => Instant.FromUtc(2020, 02, 20, 17, 42, 59);
+ }
+
+ public class Mutation
+ {
+ public Instant Test(Instant arg)
+ {
+ return arg + Duration.FromMinutes(10);
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public InstantTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturnsUtc()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: one }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-20T17:42:59Z", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-02-21T17:42:59Z")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-21T17:52:59Z", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Instant!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-02-20T17:42:59")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-20T17:52:59Z", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to Instant", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs
new file mode 100644
index 00000000000..f81f80db203
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/IsoDayOfWeekTypeTests.cs
@@ -0,0 +1,139 @@
+using System;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class IsoDayOfWeekTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public IsoDayOfWeek Monday => IsoDayOfWeek.Monday;
+ public IsoDayOfWeek Sunday => IsoDayOfWeek.Sunday;
+ public IsoDayOfWeek Friday => IsoDayOfWeek.Friday;
+ public IsoDayOfWeek None => IsoDayOfWeek.None;
+ }
+
+ public class Mutation
+ {
+ public IsoDayOfWeek Test(IsoDayOfWeek arg)
+ {
+ var intRepr = (int)arg;
+ var nextIntRepr = Math.Max(1, (intRepr + 1) % 8);
+ return (IsoDayOfWeek)nextIntRepr;
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public IsoDayOfWeekTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturnsMonday()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: monday }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal(1, queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsSunday()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: sunday }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal(7, queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsFriday()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: friday }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal(5, queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryDoesntReturnNone()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: none }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.NotEmpty(queryResult!.Errors);
+ }
+
+ [Fact]
+ public void MutationParsesMonday()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }")
+ .SetVariableValue("arg", 1)
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal(2, queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationParsesSunday()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }")
+ .SetVariableValue("arg", 7)
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal(1, queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void MutationDoesntParseZero()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }")
+ .SetVariableValue("arg", 0)
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void MutationDoesntParseEight()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }")
+ .SetVariableValue("arg", 8)
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void MutationDoesntParseNegativeNumbers()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: IsoDayOfWeek!) { test(arg: $arg) }")
+ .SetVariableValue("arg", -2)
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs
new file mode 100644
index 00000000000..b128ad4e8ce
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTimeTypeTests.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class LocalDateTimeTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public LocalDateTime One => LocalDateTime.FromDateTime(new DateTime(2020, 02, 20, 17, 42, 59));
+ }
+
+ public class Mutation
+ {
+ public LocalDateTime Test(LocalDateTime arg)
+ {
+ return arg + Period.FromMinutes(10);
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public LocalDateTimeTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: one }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-20T17:42:59", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-02-21T17:42:59")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-21T17:52:59", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-02-20T17:42:59Z")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-20T17:52:59", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59Z\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to LocalDateTime", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs
new file mode 100644
index 00000000000..5671a5706d5
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalDateTypeTests.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class LocalDateTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public LocalDate One => LocalDate.FromDateTime(new DateTime(2020, 02, 20, 17, 42, 59));
+ }
+
+ public class Mutation
+ {
+ public LocalDate Test(LocalDate arg)
+ {
+ return arg + Period.FromDays(3);
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public LocalDateTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: one }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-20", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalDate!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-02-21")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-24", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalDate!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-02-20T17:42:59")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-02-20\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-02-23", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-02-20T17:42:59\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to LocalDate", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs
new file mode 100644
index 00000000000..74fca6eb1f9
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/LocalTimeTypeTests.cs
@@ -0,0 +1,119 @@
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class LocalTimeTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public LocalTime One => LocalTime.FromHourMinuteSecondMillisecondTick(12, 42, 13, 31, 100);
+ }
+
+ public class Mutation
+ {
+ public LocalTime Test(LocalTime arg)
+ {
+ return arg + Period.FromMinutes(10);
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public LocalTimeTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: one }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("12:42:13.03101", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "12:42:13.03101")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("12:52:13.03101", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariableWithoutTicks()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "12:42:13")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("12:52:13", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: LocalTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "12:42")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"12:42:13.03101\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("12:52:13.03101", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesLiteralWithoutTick()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"12:42:13\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("12:52:13", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"12:42\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to LocalTime", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs
new file mode 100644
index 00000000000..46eb18a82fb
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/NodaTimeRequestExecutorBuilderExtensions.cs
@@ -0,0 +1,23 @@
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public static class NodaTimeRequestExecutorBuilderExtensions
+ {
+ public static ISchemaBuilder AddNodaTime(this ISchemaBuilder schemaBuilder)
+ {
+ return schemaBuilder
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType()
+ .AddType();
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs
new file mode 100644
index 00000000000..2ed669eeab5
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTimeTypeTests.cs
@@ -0,0 +1,134 @@
+using System;
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class OffsetDateTimeTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public OffsetDateTime Hours =>
+ OffsetDateTime.FromDateTimeOffset(
+ new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2)));
+
+ public OffsetDateTime HoursAndMinutes =>
+ OffsetDateTime.FromDateTimeOffset(
+ new DateTimeOffset(2020, 12, 31, 18, 30, 13, TimeSpan.FromHours(2) + TimeSpan.FromMinutes(30)));
+ }
+
+ public class Mutation
+ {
+ public OffsetDateTime Test(OffsetDateTime arg)
+ {
+ return arg + Duration.FromMinutes(10);
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public OffsetDateTimeTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hours }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:30:13+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsWithMinutes()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:30:13+02:30", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31T18:30:13+02")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariableWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31T18:30:13+02:35")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31T18:30:13")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:40:13+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesLiteralWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13+02:35\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:40:13+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31T18:30:13\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to OffsetDateTime", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs
new file mode 100644
index 00000000000..1789f6c91e6
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetDateTypeTests.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class OffsetDateTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public OffsetDate Hours =>
+ new OffsetDate(
+ LocalDate.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)),
+ Offset.FromHours(2));
+
+ public OffsetDate HoursAndMinutes =>
+ new OffsetDate(
+ LocalDate.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)),
+ Offset.FromHoursAndMinutes(2, 35));
+ }
+
+ public class Mutation
+ {
+ public OffsetDate Test(OffsetDate arg) => arg;
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public OffsetDateTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hours }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsWithMinutes()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31+02")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariableWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31+02:35")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetDate!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31+02\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesLiteralWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31+02:35\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to OffsetDate", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs
new file mode 100644
index 00000000000..0a617f38a18
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTimeTypeTests.cs
@@ -0,0 +1,132 @@
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class OffsetTimeTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public OffsetTime Hours =>
+ new OffsetTime(
+ LocalTime.FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100),
+ Offset.FromHours(2));
+
+ public OffsetTime HoursAndMinutes =>
+ new OffsetTime(
+ LocalTime.FromHourMinuteSecondMillisecondTick(18, 30, 13, 10, 100),
+ Offset.FromHoursAndMinutes(2, 35));
+ }
+
+ public class Mutation
+ {
+ public OffsetTime Test(OffsetTime arg) => arg;
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public OffsetTimeTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hours }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("18:30:13+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsWithMinutes()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("18:30:13+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "18:30:13+02")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("18:30:13+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariableWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "18:30:13+02:35")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("18:30:13+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: OffsetTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "18:30:13")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"18:30:13+02\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("18:30:13+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesLiteralWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"18:30:13+02:35\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("18:30:13+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"18:30:13\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to OffsetTime", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs
new file mode 100644
index 00000000000..7337936a45c
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/OffsetTypeTests.cs
@@ -0,0 +1,126 @@
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class OffsetTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public Offset Hours => Offset.FromHours(2);
+ public Offset HoursAndMinutes => Offset.FromHoursAndMinutes(2, 35);
+ }
+
+ public class Mutation
+ {
+ public Offset Test(Offset arg)
+ => arg + Offset.FromHoursAndMinutes(1, 5);
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public OffsetTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hours }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("+02", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsWithMinutes()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: hoursAndMinutes }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("+02:35", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "+02")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("+03:05", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariableWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "+02:35")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("+03:40", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Offset!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "18:30:13+02")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"+02\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("+03:05", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesLiteralWithMinutes()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"+02:35\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("+03:40", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"18:30:13+02\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to Offset", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs
new file mode 100644
index 00000000000..a9e25bd8226
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/PeriodTypeTests.cs
@@ -0,0 +1,94 @@
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class PeriodTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public Period One => Period.FromWeeks(-3) + Period.FromDays(3) + Period.FromTicks(139);
+ }
+
+ public class Mutation
+ {
+ public Period Test(Period arg)
+ => arg + Period.FromMinutes(-10);
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public PeriodTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: one }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("P-3W3DT139t", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Period!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "P-3W15DT139t")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("P-3W15DT-10M139t", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: Period!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "-3W3DT-10M139t")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"P-3W15DT139t\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("P-3W15DT-10M139t", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"-3W3DT-10M139t\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to Period", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs
new file mode 100644
index 00000000000..f028026b074
--- /dev/null
+++ b/src/HotChocolate/Core/test/Types.NodaTime.Tests/ZonedDateTimeTypeTests.cs
@@ -0,0 +1,138 @@
+using System;
+using System.Linq;
+using HotChocolate.Execution;
+using Microsoft.Extensions.DependencyInjection;
+using NodaTime;
+using Xunit;
+
+namespace HotChocolate.Types.NodaTime.Tests
+{
+ public class ZonedDateTimeTypeIntegrationTests
+ {
+ public static class Schema
+ {
+ public class Query
+ {
+ public ZonedDateTime Rome =>
+ new ZonedDateTime(
+ LocalDateTime.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)),
+ DateTimeZoneProviders.Tzdb["Asia/Kathmandu"],
+ Offset.FromHoursAndMinutes(5, 45));
+
+ public ZonedDateTime Utc =>
+ new ZonedDateTime(
+ LocalDateTime.FromDateTime(new DateTime(2020, 12, 31, 18, 30, 13)),
+ DateTimeZoneProviders.Tzdb["UTC"],
+ Offset.FromHours(0));
+ }
+
+ public class Mutation
+ {
+ public ZonedDateTime Test(ZonedDateTime arg)
+ {
+ return arg + Duration.FromMinutes(10);
+ }
+ }
+ }
+
+ private readonly IRequestExecutor testExecutor;
+ public ZonedDateTimeTypeIntegrationTests()
+ {
+ testExecutor = SchemaBuilder.New()
+ .AddQueryType()
+ .AddMutationType()
+ .AddNodaTime()
+ .Create()
+ .MakeExecutable();
+ }
+
+ [Fact]
+ public void QueryReturns()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: rome }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:30:13 Asia/Kathmandu +05:45", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void QueryReturnsUtc()
+ {
+ IExecutionResult? result = testExecutor.Execute("query { test: utc }");
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T18:30:13 UTC +00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31T19:30:13 Asia/Kathmandu +05:45")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T19:40:13 Asia/Kathmandu +05:45", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesVariableWithUTC()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31T19:30:13 UTC +00")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T19:40:13 UTC +00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseAnIncorrectVariable()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation($arg: ZonedDateTime!) { test(arg: $arg) }")
+ .SetVariableValue("arg", "2020-12-31T19:30:13 UTC")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ }
+
+ [Fact]
+ public void ParsesLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 Asia/Kathmandu +05:45\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T19:40:13 Asia/Kathmandu +05:45", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void ParsesLiteralWithUTC()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 UTC +00\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Equal("2020-12-31T19:40:13 UTC +00", queryResult!.Data!["test"]);
+ }
+
+ [Fact]
+ public void DoesntParseIncorrectLiteral()
+ {
+ IExecutionResult? result = testExecutor
+ .Execute(QueryRequestBuilder.New()
+ .SetQuery("mutation { test(arg: \"2020-12-31T19:30:13 UTC\") }")
+ .Create());
+ var queryResult = result as IReadOnlyQueryResult;
+ Assert.Null(queryResult!.Data);
+ Assert.Equal(1, queryResult!.Errors!.Count);
+ Assert.Null(queryResult.Errors.First().Code);
+ Assert.Equal("Unable to deserialize string to ZonedDateTime", queryResult.Errors.First().Message);
+ }
+ }
+}
diff --git a/website/src/docs/hotchocolate/defining-a-schema/interfaces.md b/website/src/docs/hotchocolate/defining-a-schema/interfaces.md
index 02457765e42..c5faa135765 100644
--- a/website/src/docs/hotchocolate/defining-a-schema/interfaces.md
+++ b/website/src/docs/hotchocolate/defining-a-schema/interfaces.md
@@ -237,7 +237,7 @@ public class Startup
> Note: We have to explicitly register the interface implementations:
>
> ```csharp
-> services.AddType()
+> services.AddGraphQLServer().AddType()
> ```
# Interfaces implementing interfaces
@@ -458,7 +458,7 @@ public class Startup
> Note: We also have to register the `DatedMessage` interface manually, if we do not expose it through a field directly:
>
> ```csharp
-> services.AddType()
+> services.AddGraphQLServer().AddType()
> ```
# Dynamic fields
diff --git a/website/src/docs/hotchocolate/defining-a-schema/scalars.md b/website/src/docs/hotchocolate/defining-a-schema/scalars.md
index 6052ac3a9ce..5a763132602 100644
--- a/website/src/docs/hotchocolate/defining-a-schema/scalars.md
+++ b/website/src/docs/hotchocolate/defining-a-schema/scalars.md
@@ -176,7 +176,44 @@ dotnet add package HotChocolate.Types.Scalars
[3]: https://tools.ietf.org/html/rfc7042#page-19
[4]: https://tools.ietf.org/html/rfc7043
-These additional scalars are not mapped by Hot Chocolate automatically, we need to [specify them manually](/docs/hotchocolate/defining-a-schema/object-types/#explicit-types).
+> Note: Most of these scalars are based on a `string`. When returning a `string` from our resolver, we need to specify the scalar type manually.
+>
+> ```csharp
+> [GraphQLType(typeof(EmailAddressType))]
+> public string GetEmail() => "test@example.com";
+> ```
+>
+> [Learn more about this](/docs/hotchocolate/defining-a-schema/object-types/#explicit-types).
+
+## NodaTime
+
+We also offer a package specifically for [NodaTime](https://github.com/nodatime/nodatime).
+
+It can be installed like the following.
+
+```bash
+dotnet add package HotChocolate.Types.NodaTime
+```
+
+**Available Scalars:**
+
+| Type | Description | Example |
+| -------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------- |
+| DateTimeZone | A [NodaTime DateTimeZone](https://nodatime.org/TimeZones) | `"Europe/Rome"` |
+| Duration | A [NodaTime Duration](https://nodatime.org/3.0.x/userguide/duration-patterns) | `"-123:07:53:10.019"` |
+| Instant | A [NodaTime Instant](https://nodatime.org/3.0.x/userguide/instant-patterns) | `"2020-02-20T17:42:59Z"` |
+| IsoDayOfWeek | A [NodaTime IsoDayOfWeek](https://nodatime.org/3.0.x/api/NodaTime.IsoDayOfWeek.html) | `7` |
+| LocalDate | A [NodaTime LocalDate](https://nodatime.org/3.0.x/userguide/localdate-patterns) | `"2020-12-25"` |
+| LocalDateTime | A [NodaTime LocalDateTime](https://nodatime.org/3.0.x/userguide/localdatetime-patterns) | `"2020-12-25T13:46:78"` |
+| LocalTime | A [NodaTime LocalTime](https://nodatime.org/3.0.x/userguide/localtime-patterns) | `"12:42:13.03101"` |
+| OffsetDateTime | A [NodaTime OffsetDateTime](https://nodatime.org/3.0.x/userguide/offsetdatetime-patterns) | `"2020-12-25T13:46:78+02:35"` |
+| OffsetDate | A [NodaTime OffsetDate](https://nodatime.org/3.0.x/userguide/offsetdate-patterns) | `"2020-12-25+02:35"` |
+| OffsetTime | A [NodaTime OffsetTime](https://nodatime.org/3.0.x/userguide/offsettime-patterns) | `"13:46:78+02:35"` |
+| Offset | A [NodeTime Offset](https://nodatime.org/3.0.x/userguide/offset-patterns) | `"+02:35"` |
+| Period | A [NodeTime Period](https://nodatime.org/3.0.x/userguide/period-patterns) | `"P-3W3DT139t"` |
+| ZonedDateTime | A [NodaTime ZonedDateTime](https://nodatime.org/3.0.x/userguide/zoneddatetime-patterns) | `"2020-12-31T19:40:13 Asia/Kathmandu +05:45"` |
+
+This package was originally developed by [@shoooe](https://github.com/shoooe).
# Binding behavior