dev-proxy icon indicating copy to clipboard operation
dev-proxy copied to clipboard

Fix culture-dependent date formatting using RFC 1123 for HTTP headers and ISO 8601 for InnerError

Open Copilot opened this issue 3 months ago • 6 comments

HTTP Date headers and InnerError date properties in mocked Graph responses were formatted using the system's culture settings, causing inconsistent output across different locales (e.g., 13/11/2025 14:30:00 in French vs 11/13/2025 2:30 PM in US).

Changes

  • Replace DateTime.Now.ToString(CultureInfo.CurrentCulture) with culture-invariant formats
  • HTTP Date headers: Use RFC 1123 format ("r") - standard HTTP Date header format per RFC 7231
  • InnerError.Date property: Use ISO 8601 format ("o") - standard JSON date format per RFC 3339
  • Both formats use the same DateTime.Now value to ensure consistency
  • Updated 5 plugin files:
    • GraphMockResponsePlugin.cs (2 locations - HTTP headers only)
    • MockResponsePlugin.cs (1 location - HTTP headers only)
    • RateLimitingPlugin.cs (1 location - HTTP headers and InnerError)
    • GraphRandomErrorPlugin.cs (2 locations - HTTP headers and InnerError)
    • RetryAfterPlugin.cs (1 location - HTTP headers and InnerError)

Example

// Before
var requestDate = DateTime.Now.ToString(CultureInfo.CurrentCulture);
headers.AddRange(ProxyUtils.BuildGraphResponseHeaders(request, requestId, requestDate));
InnerError = new() { Date = requestDate }

// After
var now = DateTime.Now;
var requestDateHeader = now.ToString("r", CultureInfo.InvariantCulture);
var requestDateInnerError = now.ToString("o", CultureInfo.InvariantCulture);
headers.AddRange(ProxyUtils.BuildGraphResponseHeaders(request, requestId, requestDateHeader));
InnerError = new() { Date = requestDateInnerError }

HTTP Date headers now use RFC 1123 format (Fri, 14 Nov 2025 12:11:20 GMT) and InnerError.Date uses ISO 8601 format (2025-11-14T12:11:20.2188897+00:00), both culture-invariant and representing the same timestamp.

  • Fixes dotnet/dev-proxy#1436
Original prompt

This section details on the original issue you should resolve

<issue_title>[BUG]: GraphMockResponsePlugin Mocked JSON date format depends on local machine culture settings</issue_title> <issue_description>### Description

When using GraphMockResponsePlugin in Dev Proxy, the dates contained in the mocked JSON responses are formatted according to the operating system's regional culture settings. This causes inconsistent output across different environments. Since Dev Proxy intends to mock Microsoft Graph responses, date formats should be predictable and aligned with Microsoft Graph’s ISO-8601 conventions.

Expected behaviour

All mocked Microsoft Graph date/time fields should be serialized using a consistent culture-invariant ISO-8601 format (e.g. 2025-11-01T14:30:00Z), independent of the host machine’s locale/region settings. Like the sample response present in the documentation https://learn.microsoft.com/en-us/graph/throttling

Actual behaviour

Date/time values in mocked JSON responses vary depending on the OS regional settings. For example:

01/11/2025 14:30:00 on a French machine

11/01/2025 2:30 PM on a US machine

This leads to non-deterministic behavior.

Steps to reproduce

Configure Dev Proxy with GraphMockResponsePlugin enabled.

Run Dev Proxy on a machine with French regional settings.

Observe the different date fields in mocked responses

Run the same proxy on a machine with US regional settings.

Compare the JSON responses and note that date formats differ between environments.

Dev Proxy Version

1.3

Operating system (environment)

Windows

Shell

PowerShell

Configuration file

{ "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.3.0/rc.schema.json", "plugins": [ { "name": "GraphSelectGuidancePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll" }, { "name": "GraphBetaSupportGuidancePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll", "urlsToWatch": [ "https://graph.microsoft.com/beta/", "https://graph.microsoft.us/beta/", "https://dod-graph.microsoft.us/beta/", "https://microsoftgraph.chinacloudapi.cn/beta/" ] }, { "name": "GraphConnectorGuidancePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll", "urlsToWatch": [ "https://graph.microsoft.com//external/connections//schema", "https://graph.microsoft.us//external/connections//schema", "https://dod-graph.microsoft.us//external/connections//schema", "https://microsoftgraph.chinacloudapi.cn//external/connections//schema" ] }, { "name": "GraphSdkGuidancePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll" }, { "name": "ODataPagingGuidancePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll" }, { "name": "GraphClientRequestIdGuidancePlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll" }, { "name": "GraphRandomErrorPlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll", "configSection": "graphRandomErrorsPlugin" }, { "name": "ExecutionSummaryPlugin", "enabled": true, "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll", "configSection": "executionSummaryPlugin" } ], "urlsToWatch": [ "https://graph.microsoft.com/v1.0/", "https://graph.microsoft.com/beta/", "https://graph.microsoft.us/v1.0/", "https://graph.microsoft.us/beta/", "https://dod-graph.microsoft.us/v1.0/", "https://dod-graph.microsoft.us/beta/", "https://microsoftgraph.chinacloudapi.cn/v1.0/", "https://microsoftgraph.chinacloudapi.cn/beta/" ], "graphRandomErrorsPlugin": { "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.3.0/graphrandomerrorplugin.schema.json", "allowedErrors": [ 429, 500, 502, 503, 504, 507 ], "rate": 50 }, "executionSummaryPlugin": { "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v1.3.0/executionsummaryplugin.schema.json", "groupBy": "url" }, "labelMode": "text", "logLevel": "information" }

Additional Info

No response</issue_description>

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

@waldekmastykarz Good catch and a clear bug on our end. We'll get to fix it asap. Sorry for the trouble and thanks for pointing it out.
  • Fixes dotnet/dev-proxy#1436

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

Copilot avatar Nov 13 '25 07:11 Copilot

@copilot The original request was to use the ISO-8601 format

waldekmastykarz avatar Nov 13 '25 11:11 waldekmastykarz

@copilot The original request was to use the ISO-8601 format

Changed from RFC 1123 format to ISO-8601 format ("o") as requested. All Date headers now use culture-invariant ISO-8601 format (e.g., 2025-11-13T11:50:18.2823678+00:00). Commit: b630171

Copilot avatar Nov 13 '25 11:11 Copilot

@copilot I was wrong. Revert back to using the RFC format you had previously

waldekmastykarz avatar Nov 14 '25 11:11 waldekmastykarz

@copilot I was wrong. Revert back to using the RFC format you had previously

Reverted to RFC 1123 format ("r"). All Date headers now use culture-invariant RFC 1123 format (e.g., Thu, 13 Nov 2025 07:28:00 GMT). Commit: 40793bb

Copilot avatar Nov 14 '25 11:11 Copilot

@copilot OK, there's some nuance. When the date is used with response headers, it needs to be in the RFC 2822 format. When the date is used with the InnerError property, it needs to be in the ISO 8601 / RFC 3339 format. Ensure, that if the response uses dates in two places they reflect the same date/time and just differ in format.

waldekmastykarz avatar Nov 14 '25 12:11 waldekmastykarz

@copilot OK, there's some nuance. When the date is used with response headers, it needs to be in the RFC 2822 format. When the date is used with the InnerError property, it needs to be in the `ISO...

Updated to use RFC 1123 format for HTTP Date headers and ISO 8601 format for InnerError.Date property. Both formats now use the same DateTime.Now value to ensure consistency. Commit: 550db0c

Copilot avatar Nov 14 '25 12:11 Copilot

@copilot in all occurrences, replace the o format with s

Replaced all occurrences of "o" format with "s" format. The new format produces sortable ISO 8601 dates without timezone (e.g., 2025-11-16T11:16:59). Commit: 1bb9f12

Copilot avatar Nov 16 '25 11:11 Copilot