Skip to content

Add precision options to DateTime/LocalDateTime/LocalTime scalars#9167

Merged
glen-84 merged 8 commits intomainfrom
gai/date-time-precision
Mar 6, 2026
Merged

Add precision options to DateTime/LocalDateTime/LocalTime scalars#9167
glen-84 merged 8 commits intomainfrom
gai/date-time-precision

Conversation

@glen-84
Copy link
Copy Markdown
Member

@glen-84 glen-84 commented Feb 20, 2026

Summary of the changes (Less than 80 chars)

  • Add precision options to DateTime/LocalDateTime/LocalTime scalars.

Copilot AI review requested due to automatic review settings February 20, 2026 14:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds precision options to the DateTime, LocalDateTime, and LocalTime scalar types in HotChocolate, allowing developers to configure the number of fractional second digits (0-7) for both input parsing and output serialization. This provides flexibility for scenarios where high precision timestamps are not required, potentially optimizing performance and storage.

Changes:

  • Added new DateTimeOptions struct to configure input and output precision for date/time scalars
  • Modified DateTime, LocalDateTime, and LocalTime scalar types to accept and apply precision options
  • Added comprehensive test coverage for precision options across all affected scalar types

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeOptions.cs New options struct defining input/output precision for date/time scalars with validation
src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs Added precision-aware constructors, regex patterns, and format strings for DateTimeType
src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs Added precision-aware constructors, regex patterns, and format strings for LocalDateTimeType
src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs Added precision-aware constructors, regex patterns, and format strings for LocalTimeType
src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeOptionsTests.cs Test coverage for DateTimeOptions validation and default values
src/HotChocolate/Core/test/Types.Tests/Types/Scalars/DateTimeTypeTests.cs Updated and expanded tests to cover precision options for DateTimeType
src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalDateTimeTypeTests.cs Updated and expanded tests to cover precision options for LocalDateTimeType
src/HotChocolate/Core/test/Types.Tests/Types/Scalars/LocalTimeTypeTests.cs Updated and expanded tests to cover precision options for LocalTimeType
src/HotChocolate/Core/src/Types/Properties/TypeResources.resx Added error messages for invalid precision values
src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs Generated resource accessors for new error messages
dictionary.txt Added "sszzz" to spell check exceptions for format string sequences
Files not reviewed (1)
  • src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs: Language not supported
Comments suppressed due to low confidence (9)

src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs:139

  • The GetLocalFormat method creates a new format string on every call for non-default precision values (except 0). Consider caching the format string in a readonly field during construction to avoid repeated string allocation and concatenation during output serialization. The same optimization could be applied to GetUtcFormat in DateTimeType and GetLocalFormat in LocalDateTimeType.
    private string GetLocalFormat()
        => _options.OutputPrecision switch
        {
            DateTimeOptions.DefaultOutputPrecision => LocalFormat,
            0 => "HH:mm:ss",
            _ => $"HH:mm:ss.{new string('F', _options.OutputPrecision)}"
        };

src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs:160

  • The GetUtcFormat and GetLocalFormat methods create new format strings on every call for non-default precision values (except 0). Consider caching these format strings in readonly fields during construction to avoid repeated string allocation and concatenation during output serialization.
    private string GetUtcFormat()
        => _options.OutputPrecision switch
        {
            DateTimeOptions.DefaultOutputPrecision => UtcFormat,
            0 => @"yyyy-MM-ddTHH\:mm\:ssZ",
            _ => @$"yyyy-MM-ddTHH\:mm\:ss.{new string('F', _options.OutputPrecision)}Z"
        };

    private string GetLocalFormat()
        => _options.OutputPrecision switch
        {
            DateTimeOptions.DefaultOutputPrecision => LocalFormat,
            0 => @"yyyy-MM-ddTHH\:mm\:sszzz",
            _ => @$"yyyy-MM-ddTHH\:mm\:ss.{new string('F', _options.OutputPrecision)}zzz"
        };

src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs:152

  • The GetLocalTimeRegex method evaluates a switch expression on every call during input parsing. Consider caching the Regex instance in a readonly field during construction to avoid the repeated switch evaluation, which would improve performance for input validation.
    private Regex GetLocalTimeRegex()
        => _options.InputPrecision switch
        {
            0 => LocalTimeRegex0(),
            1 => LocalTimeRegex1(),
            2 => LocalTimeRegex2(),
            3 => LocalTimeRegex3(),
            4 => LocalTimeRegex4(),
            5 => LocalTimeRegex5(),
            6 => LocalTimeRegex6(),
            _ => LocalTimeRegex7()
        };

src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs:154

  • The GetLocalDateTimeRegex method evaluates a switch expression on every call during input parsing. Consider caching the Regex instance in a readonly field during construction to avoid the repeated switch evaluation, which would improve performance for input validation.
    private Regex GetLocalDateTimeRegex()
        => _options.InputPrecision switch
        {
            0 => LocalDateTimeRegex0(),
            1 => LocalDateTimeRegex1(),
            2 => LocalDateTimeRegex2(),
            3 => LocalDateTimeRegex3(),
            4 => LocalDateTimeRegex4(),
            5 => LocalDateTimeRegex5(),
            6 => LocalDateTimeRegex6(),
            _ => LocalDateTimeRegex7()
        };

src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs:40

  • When InputPrecision is 0, the Pattern property will contain an invalid regex pattern \d{1,0} which means "match 1 to 0 digits". This is semantically invalid as the maximum cannot be less than the minimum. The pattern construction should handle precision 0 as a special case to completely omit the fractional seconds part, similar to how the GeneratedRegex methods handle it.
        Pattern = @"^\d{2}:\d{2}:\d{2}(?:\.\d{1," + options.Value.InputPrecision + "})?$";

src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs:41

  • When InputPrecision is 0, the Pattern property will contain an invalid regex pattern \d{1,0} which means "match 1 to 0 digits". This is semantically invalid as the maximum cannot be less than the minimum. The pattern construction should handle precision 0 as a special case to completely omit the fractional seconds part, similar to how the GeneratedRegex methods handle it.
        Pattern = @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1," + options.Value.InputPrecision + "})?$";

src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs:44

  • When InputPrecision is 0, the Pattern property will contain an invalid regex pattern \d{1,0} which means "match 1 to 0 digits". This is semantically invalid as the maximum cannot be less than the minimum. The pattern construction should handle precision 0 as a special case to completely omit the fractional seconds part, similar to how the GeneratedRegex methods handle it.
        Pattern =
            @"^\d{4}-\d{2}-\d{2}[Tt]\d{2}:\d{2}:\d{2}(?:\.\d{1,"
            + options.Value.InputPrecision
            + @"})?(?:[Zz]|[+-]\d{2}:\d{2})$";

src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs:141

  • The GetLocalFormat method creates a new format string on every call for non-default precision values (except 0). Consider caching the format string in a readonly field during construction to avoid repeated string allocation and concatenation during output serialization.
    private string GetLocalFormat()
        => _options.OutputPrecision switch
        {
            DateTimeOptions.DefaultOutputPrecision => LocalFormat,
            0 => @"yyyy-MM-ddTHH\:mm\:ss",
            _ => @$"yyyy-MM-ddTHH\:mm\:ss.{new string('F', _options.OutputPrecision)}"
        };

src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs:173

  • The GetDateTimeRegex method evaluates a switch expression on every call during input parsing. Consider caching the Regex instance in a readonly field during construction to avoid the repeated switch evaluation, which would improve performance for input validation.
    private Regex GetDateTimeRegex()
        => _options.InputPrecision switch
        {
            0 => DateTimeRegex0(),
            1 => DateTimeRegex1(),
            2 => DateTimeRegex2(),
            3 => DateTimeRegex3(),
            4 => DateTimeRegex4(),
            5 => DateTimeRegex5(),
            6 => DateTimeRegex6(),
            _ => DateTimeRegex7()
        };

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 20, 2026

Fusion Gateway Performance Results

Simple Composite Query

Req/s Err%
Constant (50 VUs) 2945.12 0.00%
Ramping (0-500-0 VUs) 3129.12 0.00%
Response Times & Query
Min Med Avg P90 P95 Max
Constant 0.72ms 14.86ms 16.75ms 30.89ms 36.17ms 171.38ms
Ramping 0.71ms 68.70ms 71.26ms 133.84ms 149.98ms 274.53ms
query TestQuery {
  topProducts(first: 5) {
    inStock
    name
    price
    shippingEstimate
    upc
    weight
    reviews {
      id
      body
      author {
        id
        username
        name
      }
    }
  }
}

Deep Recursion Query

Req/s Err%
Constant (50 VUs) 744.10 0.00%
Ramping (0-500-0 VUs) 831.25 0.00%
Response Times & Query
Min Med Avg P90 P95 Max
Constant 9.17ms 62.92ms 65.67ms 80.63ms 88.62ms 369.84ms
Ramping 1.83ms 253.86ms 258.52ms 511.37ms 549.52ms 693.49ms
query TestQuery {
  users {
    id
    username
    name
    reviews {
      id
      body
      product {
        inStock
        name
        price
        shippingEstimate
        upc
        weight
        reviews {
          id
          body
          author {
            id
            username
            name
            reviews {
              id
              body
              product {
                inStock
                name
                price
                shippingEstimate
                upc
                weight
              }
            }
          }
        }
      }
    }
  }
  topProducts(first: 5) {
    inStock
    name
    price
    shippingEstimate
    upc
    weight
    reviews {
      id
      body
      author {
        id
        username
        name
        reviews {
          id
          body
          product {
            inStock
            name
            price
            shippingEstimate
            upc
            weight
          }
        }
      }
    }
  }
}

Variable Batching Throughput

Req/s Err%
Constant (50 VUs) 23565.85 0.00%
Ramping (0-500-0 VUs) 18644.41 0.00%
Response Times & Query
Min Med Avg P90 P95 Max
Constant 0.10ms 1.71ms 2.07ms 3.96ms 4.82ms 49.24ms
Ramping 0.10ms 9.29ms 11.33ms 23.32ms 28.22ms 109.75ms
query TestQuery($upc: ID!, $price: Long!, $weight: Long!) {
  productByUpc(upc: $upc) {
    inStock
    shippingEstimate(weight: $weight, price: $price)
  }
}

Variables (5 sets batched per request)

[
  { "upc": "1", "price": 899, "weight": 100 },
  { "upc": "2", "price": 1299, "weight": 1000 },
  { "upc": "3", "price": 15, "weight": 20 },
  { "upc": "4", "price": 499, "weight": 100 },
  { "upc": "5", "price": 1299, "weight": 1000 }
]

Run 22236252464 • Commit 7622694 • Fri, 20 Feb 2026 18:50:02 GMT

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated no new comments.

Files not reviewed (1)
  • src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@glen-84 glen-84 marked this pull request as draft March 5, 2026 15:50
@glen-84 glen-84 marked this pull request as ready for review March 6, 2026 08:49
@glen-84 glen-84 requested a review from Copilot March 6, 2026 08:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 11 changed files in this pull request and generated 3 comments.

Files not reviewed (1)
  • src/HotChocolate/Core/src/Types/Properties/TypeResources.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/HotChocolate/Core/src/Types/Types/Scalars/DateTimeType.cs
Comment thread src/HotChocolate/Core/src/Types/Types/Scalars/LocalTimeType.cs
Comment thread src/HotChocolate/Core/src/Types/Types/Scalars/LocalDateTimeType.cs
@glen-84 glen-84 merged commit e161bcc into main Mar 6, 2026
127 checks passed
@glen-84 glen-84 deleted the gai/date-time-precision branch March 6, 2026 09:11
@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 6, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (6a3b7a3) to head (e2ced9e).
⚠️ Report is 895 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #9167       +/-   ##
==========================================
- Coverage   74.16%       0   -74.17%     
==========================================
  Files        2677       0     -2677     
  Lines      140790       0   -140790     
  Branches    16371       0    -16371     
==========================================
- Hits       104421       0   -104421     
+ Misses      30774       0    -30774     
+ Partials     5595       0     -5595     
Flag Coverage Δ
unittests ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants