Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Oct 24, 2025

Fix exception when using DateTime.MinValue/MaxValue with timezone offsets

Summary

Successfully fixed the ArgumentOutOfRangeException that occurred when using DateTime values near MinValue or MaxValue in environments with non-UTC timezone offsets.

Changes Made

  • Updated DateTime constructor in DateTimeValue class with optimized approach
    • Checks if value is within one day of boundaries before using try-catch
    • Uses direct conversion for normal dates (performance optimization)
    • Uses try-catch only for values near boundaries where overflow might occur
    • Catches ArgumentOutOfRangeException and maps to appropriate boundary value
    • Handles any DateTime near boundaries, not just exact MinValue/MaxValue
  • Added comprehensive unit tests to validate the fix
    • ShouldHandleDateTimeMinValueWithPositiveTimezoneOffset: Tests DateTime.MinValue with +2 offset
    • ShouldHandleDateTimeNearMinValueWithPositiveTimezoneOffset: Tests DateTime.MinValue + 1 hour with +2 offset
    • ShouldHandleDateTimeMaxValueWithNegativeTimezoneOffset: Tests DateTime.MaxValue with -2 offset
    • ShouldHandleDateTimeNearMaxValueWithNegativeTimezoneOffset: Tests DateTime.MaxValue - 1 hour with -2 offset
  • All 1265 tests pass (4 tests added)
  • CodeQL security scan: No issues found
  • Code review: Addressed feedback about handling overflow and optimizing performance

Testing

The fix was validated by:

  1. Running the original failing code pattern with UTC+2 timezone - now succeeds
  2. Running all unit tests (1265 tests) - all pass
  3. Testing with DateTime.MinValue + 1 hour with +2 offset - now succeeds
  4. Testing with DateTime.MaxValue - 1 hour with -2 offset - now succeeds
  5. Testing normal dates to verify direct conversion path works correctly
  6. Security scanning with CodeQL - no vulnerabilities

Root Cause

When a DateTime with Kind.Unspecified is implicitly converted to DateTimeOffset, it uses the local system timezone. For DateTime values near the boundaries with incompatible timezone offsets, the UTC representation falls outside the valid range, causing ArgumentOutOfRangeException.

Performance

The implementation is optimized to avoid exception overhead for the common case:

  • Normal dates (not near boundaries): Direct conversion without try-catch

  • Dates within 1 day of MinValue: Try-catch to handle potential underflow

  • Dates within 1 day of MaxValue: Try-catch to handle potential overflow

  • Fixes Exception on DateTime.MinValue #773

Original prompt

This section details on the original issue you should resolve

<issue_title>Exception on DateTime.MinValue</issue_title>
<issue_description>The code to reproduce the issue is very simple:

using Fluid;

var model = new { foo = "bar", date = DateTime.MinValue };

var template = new FluidParser().Parse("{{foo}} {{date}}");

template.Render(new TemplateContext(model))

The exception:

System.ArgumentOutOfRangeException: The UTC time represented when the offset is applied must be between year 0 and 10,000. (Parameter 'offset')
at System.DateTimeOffset.ValidateDate(DateTime dateTime, TimeSpan offset)
at System.DateTimeOffset..ctor(DateTime dateTime)
at System.DateTimeOffset.op_Implicit(DateTime dateTime)
at Fluid.Values.FluidValue.Create(Object value, TemplateOptions options)
at Fluid.Values.ObjectValueBase.GetValueAsync(String name, TemplateContext context)
at Fluid.Ast.IdentifierSegment.ResolveAsync(FluidValue value, TemplateContext context)
at Fluid.Ast.MemberExpression.EvaluateAsync(TemplateContext context)
at Fluid.Ast.OutputStatement.WriteToAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
at Fluid.Parser.FluidTemplate.RenderAsync(TextWriter writer, TextEncoder encoder, TemplateContext context)
at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TextWriter textWriter, TemplateContext context, TextEncoder encoder, Boolean isolateContext)
at Fluid.FluidTemplateExtensions.RenderAsync(IFluidTemplate template, TemplateContext context, TextEncoder encoder, Boolean isolateContext)
at Program.<Main>$(String[] args)

An exception is thrown only when the tempalte string contains the date field.

I am using .NET 9.

My timezone is (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius.

The following workaround makes it work:

using Fluid;

var model = new { foo = "bar", date = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc) };    //same value but Kind = Utc

var template = new FluidParser().Parse("{{foo}} {{date}}");

template.Render(new TemplateContext(model))

</issue_description>

<agent_instructions>- Create a unit test that sets the current thread to a Timezone offset of +2 (like the original issue states) and assigns a DateTime to reproduce the exception and ensure it is fixed.

  • Fix the issue by adding a new vallued constructor with a DateTime and checks for out of bound values when it's converted to DateTimeOffset.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@sebastienros Internally the `DateTime` is converted to `DateTimeOffset`. Since you didn't set the `Kind` it will be `unspecified` and `DateTimeOffset` will assume local. So with an offset of `2` (2 hours after UTC). At that point it will fail because you represent the smallest value, but in your timezone. In UTC it's less than the minimal possible value and `DateTimeOffset` will fail.

In that case I would suggest to use DateTimeOffset.MinValue instead. Fluid could throw a different exception, but it won't be much help, and trying to convert/round any invalid value could be worse.</comment_new>
<comment_new>@sebastienros

So I just thought it would be better to be able to handle DateTime.MinValue without an exception.

That's something that could be better, check for this specific values, which would match what you put in it and do the DateTimeOffset conversion explicitly by using DateTimeOffset.MinValue. Same for MaxValue for timezeones that are on the other side of UTC.

Obviously it's not something that we are the first to discover ... https://stackoverflow.com/questions/6924198/converting-datetime-minvalue-to-datetimeoffset

So the action to take is to change the conversion to handle these specific cases.</comment_new>
<comment_new>@sebastienros
@sksk571

the library performs datetime conversion to UTC in certain cases

I think this is a misunderstanding. Can you update your example with the actual results your are getting and what is your current timezone? And what you would have expected to see, I will then explain the reasoning behind the results.</comment_new>


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI changed the title [WIP] Fix exception on DateTime.MinValue handling in template rendering Fix ArgumentOutOfRangeException when using DateTime.MinValue/MaxValue with timezone offsets Oct 24, 2025
Copilot AI requested a review from sebastienros October 24, 2025 16:29
@sebastienros sebastienros marked this pull request as ready for review October 24, 2025 16:36

var context = new TemplateContext { TimeZone = plusTwoTimezone };
context.SetValue("foo", "bar");
context.SetValue("date", DateTime.MinValue);
Copy link
Owner

Choose a reason for hiding this comment

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

@copilot what it the value is DateTime.MinValue + 1hour, there would still be an exception. The constructor need to check if there is an overflow, not just special values like MinValue and MaxValue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the implementation to use a try-catch approach instead of checking for exact MinValue/MaxValue. Now handles any DateTime value that would cause overflow when converted to DateTimeOffset, including cases like DateTime.MinValue + 1 hour.

Added tests for these scenarios:

  • DateTime.MinValue + 1 hour with +2 offset
  • DateTime.MaxValue - 1 hour with -2 offset

Fixed in commit 710c03e

…catch

Changed from checking exact MinValue/MaxValue to using try-catch approach that handles any DateTime value that would cause overflow when converted to DateTimeOffset with timezone offset. Added tests for DateTime.MinValue + 1 hour and DateTime.MaxValue - 1 hour scenarios.

Co-authored-by: sebastienros <[email protected]>
_value = value;
}

public DateTimeValue(DateTime value)
Copy link
Owner

@sebastienros sebastienros Oct 24, 2025

Choose a reason for hiding this comment

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

@copilot Check for values less than one day of the boundaries before doing the try/catch. Otherwise just do the conversion directly.

Copy link
Owner

Choose a reason for hiding this comment

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

@copilot Check for values less than one day of the boundaries before doing the try/catch. Otherwise just do the conversion directly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Optimized the implementation to check if the value is within one day of the boundaries before using try-catch. Normal DateTime values now use direct conversion without exception overhead, while values near MinValue/MaxValue still use try-catch to handle potential overflow.

Fixed in commit 4b66dc8

Check if value is within one day of MinValue or MaxValue before using try-catch. For normal DateTime values (the common case), use direct conversion to avoid exception overhead. This improves performance while still handling edge cases correctly.

Co-authored-by: sebastienros <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exception on DateTime.MinValue

2 participants