Skip to content

PropertyTypeConverter - Prioritize IFormattable ToString when Format specified#6116

Merged
snakefoot merged 1 commit intoNLog:devfrom
snakefoot:ConvertDateTimeOffset
Mar 12, 2026
Merged

PropertyTypeConverter - Prioritize IFormattable ToString when Format specified#6116
snakefoot merged 1 commit intoNLog:devfrom
snakefoot:ConvertDateTimeOffset

Conversation

@snakefoot
Copy link
Copy Markdown
Contributor

@snakefoot snakefoot commented Mar 11, 2026

Trying to resolve #6115 (Prioritize Format-specifier, but also support conversion without having specified Format)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 11, 2026

Walkthrough

ChangeObjectType now converts non-string IFormattable inputs by first formatting them to string (with provided format and formatProvider) and attempting string-based conversion; adds direct DateTime → DateTimeOffset handling; removes the previous unconditional ToString fallback path. Two unit tests added for DateTime→DateTimeOffset conversions.

Changes

Cohort / File(s) Summary
Type Conversion Logic
src/NLog/Config/PropertyTypeConverter.cs
Refined conversion flow: handle non-string IFormattable by deriving a formatted string and retrying string conversion; special-case DateTimeDateTimeOffset conversion; preserve IConvertible and string parsing paths; remove the final unconditional ToString(format, formatProvider) fallback.
Unit Tests
tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs
Added Convert_DateTimeToDateTimeOffset and Convert_DateTimeToDateTimeOffset_NoFormat to validate DateTime→DateTimeOffset conversions with and without a format specifier.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇
I chewed a DateTime, then gave it a hop,
Puffed it to Offset — no more values drop.
Two little tests guard my clever feat,
I format, I nudge, I keep timestamps neat.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description references issue #6115 and its resolution goal, which directly relates to the code changes for DateTime to DateTimeOffset conversion handling.
Linked Issues check ✅ Passed The code changes successfully implement the suggested fix from issue #6115 by adding explicit DateTime to DateTimeOffset conversion handling with IFormattable support when format is specified.
Out of Scope Changes check ✅ Passed All changes are directly scoped to resolving issue #6115: PropertyTypeConverter logic refinement and two targeted unit tests validating DateTime to DateTimeOffset conversion.
Title check ✅ Passed The title accurately describes the main change: prioritizing IFormattable.ToString when a format is specified during type conversion, which is the core fix implemented in PropertyTypeConverter.ChangeObjectType.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@snakefoot snakefoot force-pushed the ConvertDateTimeOffset branch from 324f8b3 to ffae147 Compare March 11, 2026 20:27
@snakefoot
Copy link
Copy Markdown
Contributor Author

@CodeRabbit resume

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/NLog/Config/PropertyTypeConverter.cs`:
- Around line 161-177: The fallback path in PropertyTypeConverter lets a raw
DateTime fall through to System.Convert.ChangeType causing InvalidCastException
when propertyType is DateTimeOffset; before the final Convert.ChangeType call,
add a check: if propertyValue is DateTime dt and propertyType ==
typeof(DateTimeOffset) (or nullable DateTimeOffset), convert by creating a
DateTimeOffset from dt (preserving Kind/UTC behavior as needed) and return it;
keep this check before calling Convert.ChangeType and after the existing
IConvertible/TryConvertToType branches so TryConvertToType still runs first.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cd4c3854-17f8-4237-a5b0-b6bff29af74c

📥 Commits

Reviewing files that changed from the base of the PR and between a43692e and ffae147.

📒 Files selected for processing (2)
  • src/NLog/Config/PropertyTypeConverter.cs
  • tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 11, 2026

✅ Actions performed

Reviews resumed.

@snakefoot snakefoot force-pushed the ConvertDateTimeOffset branch from a3ede2b to 1b50176 Compare March 11, 2026 20:44
@snakefoot
Copy link
Copy Markdown
Contributor Author

@CodeRabbit Full Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 11, 2026

✅ Actions performed

Full review triggered.

@snakefoot snakefoot force-pushed the ConvertDateTimeOffset branch from 1b50176 to 4d07198 Compare March 11, 2026 20:52
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs (1)

226-236: Cover the raw DateTime -> DateTimeOffset fallback as well.

This test only exercises the formatted IFormattable path. The new special-case on Line 164 in src/NLog/Config/PropertyTypeConverter.cs is a separate branch, so adding a no-format case here would keep that regression fix protected too.

➕ Suggested test coverage
 [Fact]
 public void Convert_DateTimeToDateTimeOffset()
 {
     // Act
     var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
     var result = _sut.Convert(dateTime, typeof(DateTimeOffset), "o", null);
 
     // Assert
     var resultTyped = Assert.IsType<DateTimeOffset>(result);
     Assert.Equal(dateTime, resultTyped.DateTime);
 }
+
+[Fact]
+public void Convert_DateTimeToDateTimeOffsetWithoutFormat()
+{
+    // Act
+    var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+    var result = _sut.Convert(dateTime, typeof(DateTimeOffset), null, null);
+
+    // Assert
+    var resultTyped = Assert.IsType<DateTimeOffset>(result);
+    Assert.Equal(dateTime, resultTyped.DateTime);
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs` around lines 226 -
236, Add a second assertion in the Convert_DateTimeToDateTimeOffset test to
exercise the non-formattable fallback path by calling _sut.Convert with the same
DateTime and target type typeof(DateTimeOffset) but with format set to null (or
an empty string), then assert the result is a DateTimeOffset and that its
DateTime equals the original dateTime; this ensures the raw
DateTime->DateTimeOffset branch in PropertyTypeConverter.Convert is covered in
addition to the formatted IFormattable path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs`:
- Around line 226-236: Add a second assertion in the
Convert_DateTimeToDateTimeOffset test to exercise the non-formattable fallback
path by calling _sut.Convert with the same DateTime and target type
typeof(DateTimeOffset) but with format set to null (or an empty string), then
assert the result is a DateTimeOffset and that its DateTime equals the original
dateTime; this ensures the raw DateTime->DateTimeOffset branch in
PropertyTypeConverter.Convert is covered in addition to the formatted
IFormattable path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ab337b15-a7a4-4786-969a-81edcc475c37

📥 Commits

Reviewing files that changed from the base of the PR and between a3ede2b and 1b50176.

📒 Files selected for processing (2)
  • src/NLog/Config/PropertyTypeConverter.cs
  • tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs

@snakefoot snakefoot force-pushed the ConvertDateTimeOffset branch 2 times, most recently from 1c2b32e to 9ec1974 Compare March 11, 2026 20:59
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs (1)

226-236: Add coverage for the no-format branch.

This test only exercises the formatted IFormattable path. The new raw DateTime -> DateTimeOffset fallback on Lines 162-163 in src/NLog/Config/PropertyTypeConverter.cs is still unpinned.

🧪 Suggested companion test
         [Fact]
         public void Convert_DateTimeToDateTimeOffset()
         {
             // Act
             var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
             var result = _sut.Convert(dateTime, typeof(DateTimeOffset), "o", null);

             // Assert
             var resultTyped = Assert.IsType<DateTimeOffset>(result);
             Assert.Equal(dateTime, resultTyped.DateTime);
         }
+
+        [Fact]
+        public void Convert_DateTimeToDateTimeOffsetWithoutFormat()
+        {
+            // Act
+            var dateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+            var result = _sut.Convert(dateTime, typeof(DateTimeOffset), null, null);
+
+            // Assert
+            var resultTyped = Assert.IsType<DateTimeOffset>(result);
+            Assert.Equal(dateTime, resultTyped.DateTime);
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs` around lines 226 -
236, Add a companion unit test that exercises the "no-format" fallback in
PropertyTypeConverter.Convert by calling _sut.Convert with a DateTime (same
sample: new DateTime(1970,1,1,..., DateTimeKind.Utc)) and passing null for the
format parameter (third arg) instead of "o"; assert the returned object is a
DateTimeOffset (use Assert.IsType<DateTimeOffset>) and that its DateTime equals
the original DateTime. Name the test clearly (e.g.,
Convert_DateTimeToDateTimeOffset_NoFormat) and mirror the existing assertions
used in Convert_DateTimeToDateTimeOffset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs`:
- Around line 226-236: Add a companion unit test that exercises the "no-format"
fallback in PropertyTypeConverter.Convert by calling _sut.Convert with a
DateTime (same sample: new DateTime(1970,1,1,..., DateTimeKind.Utc)) and passing
null for the format parameter (third arg) instead of "o"; assert the returned
object is a DateTimeOffset (use Assert.IsType<DateTimeOffset>) and that its
DateTime equals the original DateTime. Name the test clearly (e.g.,
Convert_DateTimeToDateTimeOffset_NoFormat) and mirror the existing assertions
used in Convert_DateTimeToDateTimeOffset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9988e79e-c33b-47e3-9428-8753a555b01d

📥 Commits

Reviewing files that changed from the base of the PR and between a43692e and 4d07198.

📒 Files selected for processing (2)
  • src/NLog/Config/PropertyTypeConverter.cs
  • tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs

@snakefoot snakefoot force-pushed the ConvertDateTimeOffset branch from 9ec1974 to 82ec559 Compare March 11, 2026 21:05
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs (1)

226-236: Consider a more explicit assertion for clarity.

The assertion Assert.Equal(dateTime.Date, resultTyped.DateTime) works but is semantically confusing—it compares a date-at-midnight with the full DateTime from DateTimeOffset. Since the "yyyy-MM-dd" format intentionally discards time, asserting against the expected concrete value makes the intent clearer:

-            Assert.Equal(dateTime.Date, resultTyped.DateTime);
+            Assert.Equal(new DateTime(1970, 1, 1), resultTyped.DateTime);

Alternatively, consider adding an offset assertion if that's relevant to the bug fix (e.g., Assert.Equal(TimeSpan.Zero, resultTyped.Offset) if UTC is expected).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs` around lines 226 -
236, The test Convert_DateTimeToDateTimeOffset is asserting dateTime.Date
against resultTyped.DateTime which is confusing; update the assertion to compare
against an explicit expected DateTimeOffset constructed from dateTime.Date (or
compare resultTyped.DateTime to dateTime.Date and resultTyped.Offset to the
expected offset) so the intent of the "yyyy-MM-dd" format is clear—modify the
assertions in the Convert_DateTimeToDateTimeOffset test that inspect
_sut.Convert(...) and the resultTyped variable to use an explicit expected
DateTimeOffset (and optionally assert resultTyped.Offset, e.g., TimeSpan.Zero if
UTC is expected).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs`:
- Around line 226-236: The test Convert_DateTimeToDateTimeOffset is asserting
dateTime.Date against resultTyped.DateTime which is confusing; update the
assertion to compare against an explicit expected DateTimeOffset constructed
from dateTime.Date (or compare resultTyped.DateTime to dateTime.Date and
resultTyped.Offset to the expected offset) so the intent of the "yyyy-MM-dd"
format is clear—modify the assertions in the Convert_DateTimeToDateTimeOffset
test that inspect _sut.Convert(...) and the resultTyped variable to use an
explicit expected DateTimeOffset (and optionally assert resultTyped.Offset,
e.g., TimeSpan.Zero if UTC is expected).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6f57ef57-39a9-4b78-a112-89eaff3d3d91

📥 Commits

Reviewing files that changed from the base of the PR and between 4d07198 and 82ec559.

📒 Files selected for processing (2)
  • src/NLog/Config/PropertyTypeConverter.cs
  • tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/NLog/Config/PropertyTypeConverter.cs

@snakefoot snakefoot force-pushed the ConvertDateTimeOffset branch from 82ec559 to 1cdec99 Compare March 12, 2026 17:39
@snakefoot
Copy link
Copy Markdown
Contributor Author

@CodeRabbit Full Review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 12, 2026

✅ Actions performed

Full review triggered.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs (1)

238-248: Consider adding an assertion for offset preservation.

The test validates that the DateTime value is preserved, but doesn't verify that the DateTimeKind.Utc is correctly converted to a zero UTC offset. DateTime.Equals compares only Ticks, not Kind.

🔧 Suggested enhancement
             // Assert
             var resultTyped = Assert.IsType<DateTimeOffset>(result);
             Assert.Equal(dateTime, resultTyped.DateTime);
+            Assert.Equal(TimeSpan.Zero, resultTyped.Offset);
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs` around lines 238 -
248, The test Convert_DateTimeToDateTimeOffset_NoFormat should also assert that
the UTC kind is preserved as a zero offset; after calling _sut.Convert(dateTime,
typeof(DateTimeOffset), null, null) and casting to resultTyped (DateTimeOffset),
add an assertion that resultTyped.Offset == TimeSpan.Zero (or that
resultTyped.ToUniversalTime().Ticks equals dateTime.ToUniversalTime().Ticks) so
the conversion from DateTime with DateTimeKind.Utc yields a DateTimeOffset with
zero UTC offset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs`:
- Around line 238-248: The test Convert_DateTimeToDateTimeOffset_NoFormat should
also assert that the UTC kind is preserved as a zero offset; after calling
_sut.Convert(dateTime, typeof(DateTimeOffset), null, null) and casting to
resultTyped (DateTimeOffset), add an assertion that resultTyped.Offset ==
TimeSpan.Zero (or that resultTyped.ToUniversalTime().Ticks equals
dateTime.ToUniversalTime().Ticks) so the conversion from DateTime with
DateTimeKind.Utc yields a DateTimeOffset with zero UTC offset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0dddfa5f-0c2d-401b-ad8b-8d3245b76459

📥 Commits

Reviewing files that changed from the base of the PR and between a43692e and 1cdec99.

📒 Files selected for processing (2)
  • src/NLog/Config/PropertyTypeConverter.cs
  • tests/NLog.UnitTests/Config/PropertyTypeConverterTests.cs

@sonarqubecloud
Copy link
Copy Markdown

@snakefoot snakefoot changed the title PropertyTypeConverter - Apply IFormattable when Format specified PropertyTypeConverter - Prioritize IFormattable ToString when Format specified Mar 12, 2026
@snakefoot snakefoot merged commit 64f068c into NLog:dev Mar 12, 2026
6 of 7 checks passed
@snakefoot snakefoot added this to the 6.1.2 milestone Mar 12, 2026
This was referenced Apr 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DatabaseTarget writes default(DateTimeOffset) when ParameterType = typeof(DateTimeOffset) with ${date} layout

1 participant