Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 17, 2025

The orchestration analyzer detects non-deterministic DateTime/DateTimeOffset usage but didn't cover System.TimeProvider (available in .NET 8+), leaving a gap in determinism validation.

Changes

  • Extended DateTimeOrchestrationAnalyzer to detect TimeProvider.GetUtcNow(), GetLocalNow(), and GetTimestamp() invocations using the existing DURABLE0001 diagnostic
  • Extended DateTimeOrchestrationFixer to replace TimeProvider calls with context.CurrentUtcDateTime, adding DateTimeOffset casts where the original method returned that type
  • Updated test framework to target .NET 8 reference assemblies for TimeProvider API availability

Example

Before (triggers warning):

[Function("Run")]
DateTimeOffset Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
    return TimeProvider.System.GetUtcNow();
}

After (code fix applied):

[Function("Run")]
DateTimeOffset Run([OrchestrationTrigger] TaskOrchestrationContext context)
{
    return (DateTimeOffset)context.CurrentUtcDateTime;
}
Original prompt

This section details on the original issue you should resolve

<issue_title>Roslyn Analyzer: System.TimeProvider</issue_title>
<issue_description>The Analyzer besides catching non-deterministic cases from System.DateTime, it should also support a diagnostic when developers use System.TimeProvider. (only available in .NET > 8.0)</issue_description>

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


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

CultureInfo.InvariantCulture,
Resources.UseInsteadFixerTitle,
recommendation,
dateTimeExpression.ToString());
CultureInfo.InvariantCulture,
Resources.UseInsteadFixerTitle,
recommendation,
timeProviderInvocation.ToString());
Comment on lines +74 to +109
else if (orchestrationContext.SyntaxNodeWithDiagnostic is InvocationExpressionSyntax timeProviderInvocation)
{
// For DateTime, we may need to add .Date for Today
string dateTimeTodaySuffix = isDateTimeToday ? ".Date" : string.Empty;
recommendation = $"{contextParameterName}.CurrentUtcDateTime{dateTimeTodaySuffix}";
}
// Determine the method being called
if (semanticModel.GetSymbolInfo(timeProviderInvocation).Symbol is IMethodSymbol methodSymbol)
{
string methodName = methodSymbol.Name;

// Check if the method returns DateTimeOffset
bool returnsDateTimeOffset = methodSymbol.ReturnType.ToDisplayString() == "System.DateTimeOffset";

// e.g: "Use 'context.CurrentUtcDateTime' instead of 'DateTime.Now'"
// e.g: "Use 'context.CurrentUtcDateTime.Date' instead of 'DateTime.Today'"
// e.g: "Use '(DateTimeOffset)context.CurrentUtcDateTime' instead of 'DateTimeOffset.Now'"
string title = string.Format(
CultureInfo.InvariantCulture,
Resources.UseInsteadFixerTitle,
recommendation,
dateTimeExpression.ToString());

context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedDocument: c => ReplaceDateTime(context.Document, orchestrationContext.Root, dateTimeExpression, contextParameterName, isDateTimeToday, isDateTimeOffset),
equivalenceKey: title), // This key is used to prevent duplicate code fixes.
context.Diagnostics);
// Build the recommendation based on the method name
string recommendation = methodName switch
{
"GetUtcNow" when returnsDateTimeOffset => $"(DateTimeOffset){contextParameterName}.CurrentUtcDateTime",
"GetUtcNow" => $"{contextParameterName}.CurrentUtcDateTime",
"GetLocalNow" when returnsDateTimeOffset => $"(DateTimeOffset){contextParameterName}.CurrentUtcDateTime.ToLocalTime()",
"GetLocalNow" => $"{contextParameterName}.CurrentUtcDateTime.ToLocalTime()",
"GetTimestamp" => $"{contextParameterName}.CurrentUtcDateTime.Ticks",
_ => $"{contextParameterName}.CurrentUtcDateTime",
};

// e.g: "Use 'context.CurrentUtcDateTime' instead of 'TimeProvider.System.GetUtcNow()'"
string title = string.Format(
CultureInfo.InvariantCulture,
Resources.UseInsteadFixerTitle,
recommendation,
timeProviderInvocation.ToString());

context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedDocument: c => ReplaceTimeProvider(context.Document, orchestrationContext.Root, timeProviderInvocation, contextParameterName, methodName, returnsDateTimeOffset),
equivalenceKey: title),
context.Diagnostics);
}
}
Copilot AI changed the title [WIP] Add Roslyn Analyzer support for System.TimeProvider Add TimeProvider support to orchestration analyzer Dec 17, 2025
Copilot AI requested a review from YunchuWang December 17, 2025 00:40
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.

Roslyn Analyzer: System.TimeProvider

2 participants