Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions Fluid.Tests/TemplateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,46 @@ public async Task ShouldEvaluateTimeSpanValue()
Assert.Equal("1970-01-01 01:00:00Z", result);
}

[Fact]
public async Task ShouldHandleDateTimeMinValueWithPositiveTimezoneOffset()
{
// Set a timezone offset of +2 hours (like EET - Eastern European Time)
var plusTwoTimezone = TimeZoneInfo.CreateCustomTimeZone("Custom+2", TimeSpan.FromHours(2), "UTC+2", "UTC+2");

_parser.TryParse("{{ foo }} {{ date }}", out var template, out var error);

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


// This should not throw ArgumentOutOfRangeException
var result = await template.RenderAsync(context);

// DateTime.MinValue should be rendered as the minimum DateTimeOffset value
Assert.Contains("bar", result);
Assert.Contains("0001-01-01", result);
}

[Fact]
public async Task ShouldHandleDateTimeMaxValueWithNegativeTimezoneOffset()
{
// Set a timezone offset of -2 hours (like Brazil Standard Time)
var minusTwoTimezone = TimeZoneInfo.CreateCustomTimeZone("Custom-2", TimeSpan.FromHours(-2), "UTC-2", "UTC-2");

_parser.TryParse("{{ foo }} {{ date }}", out var template, out var error);

var context = new TemplateContext { TimeZone = minusTwoTimezone };
context.SetValue("foo", "bar");
context.SetValue("date", DateTime.MaxValue);

// This should not throw ArgumentOutOfRangeException
var result = await template.RenderAsync(context);

// DateTime.MaxValue should be rendered as the maximum DateTimeOffset value
Assert.Contains("bar", result);
Assert.Contains("9999-12-31", result);
}

[Fact]
public async Task ShouldEvaluateObjectProperty()
{
Expand Down
22 changes: 22 additions & 0 deletions Fluid/Values/DateTimeValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ public DateTimeValue(DateTimeOffset value)
_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

{
// Handle edge cases where DateTime.MinValue or DateTime.MaxValue
// cannot be safely converted to DateTimeOffset with local timezone offset

if (value == DateTime.MinValue)
{
// Use DateTimeOffset.MinValue to avoid offset issues
_value = DateTimeOffset.MinValue;
}
else if (value == DateTime.MaxValue)
{
// Use DateTimeOffset.MaxValue to avoid offset issues
_value = DateTimeOffset.MaxValue;
}
else
{
// Normal conversion - implicit conversion uses local timezone for Unspecified kind
_value = value;
}
}

public override FluidValues Type => FluidValues.DateTime;

public override bool Equals(FluidValue other)
Expand Down