diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs
index 18a5895d861..59e3d3ade36 100644
--- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs
+++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs
@@ -321,7 +321,7 @@ internal static string DataLoaderResolverContextExtensions_UnableToRegister {
}
///
- /// Looks up a localized string similar to InputPrecision must be less than or equal to 7..
+ /// Looks up a localized string similar to InputPrecision must be less than or equal to 9..
///
internal static string DateTimeOptions_InputPrecision_InvalidValue {
get {
diff --git a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
index 5df0307fe49..4ceae5fbbb0 100644
--- a/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
+++ b/src/HotChocolate/Core/src/Types/Properties/TypeResources.resx
@@ -1069,7 +1069,7 @@ Type: `{0}`
The stability must follow the GraphQL type name rules.
- InputPrecision must be less than or equal to 7.
+ InputPrecision must be less than or equal to 9.
OutputPrecision must be less than or equal to 7.
diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeOptions.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeOptions.cs
index 32bd19d18b6..e8b332798de 100644
--- a/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeOptions.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeOptions.cs
@@ -8,7 +8,9 @@ namespace HotChocolate.Types;
///
public struct DateTimeOptions
{
- public const byte DefaultInputPrecision = 7;
+ public const byte DefaultInputPrecision = 9;
+
+ // DateTimeOffset, DateTime, and TimeOnly all have a maximum of 7 fractional second digits.
public const byte DefaultOutputPrecision = 7;
public DateTimeOptions()
@@ -17,17 +19,20 @@ public DateTimeOptions()
///
/// Gets the maximum number of fractional second digits to expect when parsing date and time
- /// input values.
+ /// input values. Note that the underlying .NET types (,
+ /// , and ) have a maximum resolution of 7
+ /// fractional digits (100-nanosecond ticks), so digits beyond the 7th are rounded during
+ /// parsing.
///
///
- /// Thrown when the value is greater than 7.
+ /// Thrown when the value is greater than 9.
///
public byte InputPrecision
{
get;
init
{
- if (value > 7)
+ if (value > 9)
{
throw new ArgumentOutOfRangeException(
nameof(InputPrecision),
diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs
index 7451fdf4375..e8980cf3476 100644
--- a/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs
@@ -196,7 +196,9 @@ private Regex GetDateTimeRegex()
4 => DateTimeRegex4(),
5 => DateTimeRegex5(),
6 => DateTimeRegex6(),
- _ => DateTimeRegex7()
+ 7 => DateTimeRegex7(),
+ 8 => DateTimeRegex8(),
+ _ => DateTimeRegex9()
};
[GeneratedRegex(
@@ -238,4 +240,14 @@ private Regex GetDateTimeRegex()
@"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,7})?(Z|[+-][0-9]{2}:[0-9]{2})\z",
RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
private static partial Regex DateTimeRegex7();
+
+ [GeneratedRegex(
+ @"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,8})?(Z|[+-][0-9]{2}:[0-9]{2})\z",
+ RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
+ private static partial Regex DateTimeRegex8();
+
+ [GeneratedRegex(
+ @"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?(Z|[+-][0-9]{2}:[0-9]{2})\z",
+ RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
+ private static partial Regex DateTimeRegex9();
}
diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs
index cdd54992016..729f5973946 100644
--- a/src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs
@@ -176,7 +176,9 @@ private Regex GetLocalDateTimeRegex()
4 => LocalDateTimeRegex4(),
5 => LocalDateTimeRegex5(),
6 => LocalDateTimeRegex6(),
- _ => LocalDateTimeRegex7()
+ 7 => LocalDateTimeRegex7(),
+ 8 => LocalDateTimeRegex8(),
+ _ => LocalDateTimeRegex9()
};
[GeneratedRegex(@"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\z",
@@ -210,4 +212,12 @@ private Regex GetLocalDateTimeRegex()
[GeneratedRegex(@"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,7})?\z",
RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
private static partial Regex LocalDateTimeRegex7();
+
+ [GeneratedRegex(@"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,8})?\z",
+ RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
+ private static partial Regex LocalDateTimeRegex8();
+
+ [GeneratedRegex(@"^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?\z",
+ RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase)]
+ private static partial Regex LocalDateTimeRegex9();
}
diff --git a/src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs b/src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs
index 42ed4e0b2d6..1bd84e81695 100644
--- a/src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs
+++ b/src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs
@@ -174,7 +174,9 @@ private Regex GetLocalTimeRegex()
4 => LocalTimeRegex4(),
5 => LocalTimeRegex5(),
6 => LocalTimeRegex6(),
- _ => LocalTimeRegex7()
+ 7 => LocalTimeRegex7(),
+ 8 => LocalTimeRegex8(),
+ _ => LocalTimeRegex9()
};
[GeneratedRegex(@"^[0-9]{2}:[0-9]{2}:[0-9]{2}\z", RegexOptions.ExplicitCapture)]
@@ -200,4 +202,10 @@ private Regex GetLocalTimeRegex()
[GeneratedRegex(@"^[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,7})?\z", RegexOptions.ExplicitCapture)]
private static partial Regex LocalTimeRegex7();
+
+ [GeneratedRegex(@"^[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,8})?\z", RegexOptions.ExplicitCapture)]
+ private static partial Regex LocalTimeRegex8();
+
+ [GeneratedRegex(@"^[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{1,9})?\z", RegexOptions.ExplicitCapture)]
+ private static partial Regex LocalTimeRegex9();
}
diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeOptionsTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeOptionsTests.cs
index 3c35caed5ed..082ed96ad0e 100644
--- a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeOptionsTests.cs
+++ b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeOptionsTests.cs
@@ -17,7 +17,7 @@ public void DefaultConstructor_ShouldSetDefaultPrecisions()
public void DefaultConstants_ShouldBeCorrect()
{
// assert
- Assert.Equal(7, DateTimeOptions.DefaultInputPrecision);
+ Assert.Equal(9, DateTimeOptions.DefaultInputPrecision);
Assert.Equal(7, DateTimeOptions.DefaultOutputPrecision);
}
@@ -30,6 +30,8 @@ public void DefaultConstants_ShouldBeCorrect()
[InlineData(5)]
[InlineData(6)]
[InlineData(7)]
+ [InlineData(8)]
+ [InlineData(9)]
public void InputPrecision_ValidValues_ShouldSet(byte precision)
{
// arrange & act
@@ -58,8 +60,6 @@ public void OutputPrecision_ValidValues_ShouldSet(byte precision)
}
[Theory]
- [InlineData(8)]
- [InlineData(9)]
[InlineData(10)]
[InlineData(255)]
public void InputPrecision_InvalidValues_ShouldThrow(byte precision)
diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeTypeTests.cs
index 2ba79dc582b..c88ee7b15a3 100644
--- a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeTypeTests.cs
+++ b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeTypeTests.cs
@@ -328,6 +328,8 @@ public void DateTime_Relaxed_Format_Check()
[InlineData(5, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,5})?(?:[Zz]|[+-]\d{2}:\d{2})$")]
[InlineData(6, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?(?:[Zz]|[+-]\d{2}:\d{2})$")]
[InlineData(7, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,7})?(?:[Zz]|[+-]\d{2}:\d{2})$")]
+ [InlineData(8, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,8})?(?:[Zz]|[+-]\d{2}:\d{2})$")]
+ [InlineData(9, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:[Zz]|[+-]\d{2}:\d{2})$")]
public void Pattern_Should_Match_InputPrecision(byte precision, string expectedPattern)
{
// arrange & act
@@ -354,8 +356,8 @@ public static TheoryData ValidInput()
},
{
DateTimeOptions.DefaultInputPrecision,
- "2023-12-24T15:30:00.1234567+01:00",
- new DateTimeOffset(2023, 12, 24, 15, 30, 0, 123, 456, TimeSpan.FromHours(1)).AddTicks(7)
+ "2023-12-24T15:30:00.123456789+01:00", // Rounded to ".1234568".
+ new DateTimeOffset(2023, 12, 24, 15, 30, 0, 123, 456, TimeSpan.FromHours(1)).AddTicks(8)
}
};
}
@@ -376,13 +378,17 @@ public static TheoryData InvalidInput()
// ReSharper disable once GrammarMistakeInComment
// Invalid date (February 30th).
{ DateTimeOptions.DefaultInputPrecision, "2023-02-30T15:30:00Z" },
- // More than 7 fractional second digits.
- { DateTimeOptions.DefaultInputPrecision, "2023-12-24T15:30:00.12345678Z" },
+ // More than 9 fractional second digits.
+ { DateTimeOptions.DefaultInputPrecision, "2023-12-24T15:30:00.1234567890Z" },
// Invalid offset (exceeds maximum).
{ DateTimeOptions.DefaultInputPrecision, "2023-12-24T15:30:00+25:00" },
// Invalid offset format.
{ DateTimeOptions.DefaultInputPrecision, "2023-12-24T15:30:00 UTC" },
// Additional cases.
+ // More than 8 fractional second digits with precision set to 8.
+ { 8, "2023-12-24T15:30:00.123456789Z" },
+ // More than 7 fractional second digits with precision set to 7.
+ { 7, "2023-12-24T15:30:00.12345678Z" },
// More than 6 fractional second digits with precision set to 6.
{ 6, "2023-12-24T15:30:00.1234567Z" },
// More than 5 fractional second digits with precision set to 5.
diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalDateTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalDateTimeTypeTests.cs
index 10b3016814d..4a6b1914dbc 100644
--- a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalDateTimeTypeTests.cs
+++ b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalDateTimeTypeTests.cs
@@ -323,6 +323,8 @@ public void LocalDateTime_Relaxed_Format_Check()
[InlineData(5, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,5})?$")]
[InlineData(6, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?$")]
[InlineData(7, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,7})?$")]
+ [InlineData(8, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,8})?$")]
+ [InlineData(9, @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?$")]
public void Pattern_Should_Match_InputPrecision(byte precision, string expectedPattern)
{
// arrange & act
@@ -375,8 +377,8 @@ public static TheoryData ValidInput()
},
{
DateTimeOptions.DefaultInputPrecision,
- "2023-12-24t15:30:00.1234567",
- new DateTime(2023, 12, 24, 15, 30, 0, 123, 456).AddTicks(7)
+ "2023-12-24t15:30:00.123456789", // Rounded to ".1234568".
+ new DateTime(2023, 12, 24, 15, 30, 0, 123, 456).AddTicks(8)
}
};
}
@@ -399,9 +401,13 @@ public static TheoryData InvalidInput()
// ReSharper disable once GrammarMistakeInComment
// Invalid date (February 30th).
{ DateTimeOptions.DefaultInputPrecision, "2023-02-30T15:30:00" },
- // More than 7 fractional second digits.
- { DateTimeOptions.DefaultInputPrecision, "2023-12-24T15:30:00.12345678" },
+ // More than 9 fractional second digits.
+ { DateTimeOptions.DefaultInputPrecision, "2023-12-24T15:30:00.1234567890" },
// Additional cases.
+ // More than 8 fractional second digits with precision set to 8.
+ { 8, "2023-12-24T15:30:00.123456789" },
+ // More than 7 fractional second digits with precision set to 7.
+ { 7, "2023-12-24T15:30:00.12345678" },
// More than 6 fractional second digits with precision set to 6.
{ 6, "2023-12-24T15:30:00.1234567" },
// More than 5 fractional second digits with precision set to 5.
diff --git a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalTimeTypeTests.cs b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalTimeTypeTests.cs
index 1b41874f8cb..969d6b29518 100644
--- a/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalTimeTypeTests.cs
+++ b/src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalTimeTypeTests.cs
@@ -323,6 +323,8 @@ public void LocalTime_Relaxed_Format_Check()
[InlineData(5, @"^\d{2}:\d{2}:\d{2}(?:\.\d{1,5})?$")]
[InlineData(6, @"^\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?$")]
[InlineData(7, @"^\d{2}:\d{2}:\d{2}(?:\.\d{1,7})?$")]
+ [InlineData(8, @"^\d{2}:\d{2}:\d{2}(?:\.\d{1,8})?$")]
+ [InlineData(9, @"^\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?$")]
public void Pattern_Should_Match_InputPrecision(byte precision, string expectedPattern)
{
// arrange & act
@@ -372,8 +374,8 @@ public static TheoryData ValidInput()
},
{
DateTimeOptions.DefaultInputPrecision,
- "07:30:00.1234567",
- new TimeOnly(7, 30, 0, 123, 456).Add(TimeSpan.FromTicks(7))
+ "07:30:00.123456789", // Rounded to ".1234568".
+ new TimeOnly(7, 30, 0, 123, 456).Add(TimeSpan.FromTicks(8))
}
};
}
@@ -395,9 +397,13 @@ public static TheoryData InvalidInput()
{ DateTimeOptions.DefaultInputPrecision, "24:00:00" },
// Invalid minute (60).
{ DateTimeOptions.DefaultInputPrecision, "15:60:00" },
- // More than 7 fractional second digits.
- { DateTimeOptions.DefaultInputPrecision, "15:30:00.12345678" },
+ // More than 9 fractional second digits.
+ { DateTimeOptions.DefaultInputPrecision, "15:30:00.1234567890" },
// Additional cases.
+ // More than 8 fractional second digits with precision set to 8.
+ { 8, "15:30:00.123456789" },
+ // More than 7 fractional second digits with precision set to 7.
+ { 7, "15:30:00.12345678" },
// More than 6 fractional second digits with precision set to 6.
{ 6, "15:30:00.1234567" },
// More than 5 fractional second digits with precision set to 5.