Search code examples
c#nswagnswagstudio

NSwagStudio Generated C# Client Offsets Dates


I am using NSwagStudio to generate a C# client and in general it's working great. However, I just discovered that it's not recognizing the timezone properly in date strings. I am getting an auth token back from the API with an expiration. That expiration string in the JSON looks like this: 2022-09-21T22:09:34.722Z. With the Z modifier there it should know that it's UTC. When I create a new application and just use DateTime.Parse() and put in that string it gives me a date that reflects correctly in my local time zone (GMT-6). However, when the client parses this object, the date value is showing as if this is my local time (i.e. the example date would show as 10:09PM my time when it should actually be 10:09PM UTC which is 4:09PM my time).

Here is my nswag.json file:

{
  "runtime": "Net60",
  "defaultVariables": null,
  "documentGenerator": {
    "fromDocument": {
      "json": "",
      "url": "*my/swagger/json/location*",
      "output": null,
      "newLineBehavior": "Auto"
    }
  },
  "codeGenerators": {
    "openApiToCSharpClient": {
      "clientBaseClass": "ClientBase",
      "configurationClass": null,
      "generateClientClasses": true,
      "generateClientInterfaces": true,
      "clientBaseInterface": "IClient",
      "injectHttpClient": true,
      "disposeHttpClient": true,
      "protectedMethods": [],
      "generateExceptionClasses": true,
      "exceptionClass": "ApiException",
      "wrapDtoExceptions": true,
      "useHttpClientCreationMethod": false,
      "httpClientType": "System.Net.Http.HttpClient",
      "useHttpRequestMessageCreationMethod": true,
      "useBaseUrl": true,
      "generateBaseUrlProperty": true,
      "generateSyncMethods": false,
      "generatePrepareRequestAndProcessResponseAsAsyncMethods": false,
      "exposeJsonSerializerSettings": false,
      "clientClassAccessModifier": "internal",
      "typeAccessModifier": "public",
      "generateContractsOutput": false,
      "contractsNamespace": null,
      "contractsOutputFilePath": null,
      "parameterDateTimeFormat": "s",
      "parameterDateFormat": "yyyy-MM-dd",
      "generateUpdateJsonSerializerSettingsMethod": true,
      "useRequestAndResponseSerializationSettings": false,
      "serializeTypeInformation": false,
      "queryNullValue": "",
      "className": "{controller}Client",
      "operationGenerationMode": "MultipleClientsFromFirstTagAndOperationId",
      "additionalNamespaceUsages": [],
      "additionalContractNamespaceUsages": [],
      "generateOptionalParameters": true,
      "generateJsonMethods": true,
      "enforceFlagEnums": false,
      "parameterArrayType": "System.Collections.Generic.IEnumerable",
      "parameterDictionaryType": "System.Collections.Generic.IDictionary",
      "responseArrayType": "System.Collections.Generic.ICollection",
      "responseDictionaryType": "System.Collections.Generic.IDictionary",
      "wrapResponses": false,
      "wrapResponseMethods": [],
      "generateResponseClasses": true,
      "responseClass": "SwaggerResponse",
      "namespace": "my.namespace",
      "requiredPropertiesMustBeDefined": true,
      "dateType": "System.DateTime",
      "jsonConverters": null,
      "anyType": "object",
      "dateTimeType": "System.DateTime",
      "timeType": "System.TimeSpan",
      "timeSpanType": "System.TimeSpan",
      "arrayType": "System.Collections.Generic.ICollection",
      "arrayInstanceType": "System.Collections.ObjectModel.Collection",
      "dictionaryType": "System.Collections.Generic.IDictionary",
      "dictionaryInstanceType": "System.Collections.Generic.Dictionary",
      "arrayBaseType": "System.Collections.ObjectModel.Collection",
      "dictionaryBaseType": "System.Collections.Generic.Dictionary",
      "classStyle": "Poco",
      "jsonLibrary": "NewtonsoftJson",
      "generateDefaultValues": true,
      "generateDataAnnotations": true,
      "excludedTypeNames": [],
      "excludedParameterNames": [],
      "handleReferences": false,
      "generateImmutableArrayProperties": false,
      "generateImmutableDictionaryProperties": false,
      "jsonSerializerSettingsTransformationMethod": null,
      "inlineNamedArrays": false,
      "inlineNamedDictionaries": false,
      "inlineNamedTuples": true,
      "inlineNamedAny": false,
      "generateDtoTypes": true,
      "generateOptionalPropertiesAsNullable": false,
      "generateNullableReferenceTypes": false,
      "templateDirectory": null,
      "typeNameGeneratorType": null,
      "propertyNameGeneratorType": null,
      "enumNameGeneratorType": null,
      "serviceHost": null,
      "serviceSchemes": null,
      "output": "Client.cs",
      "newLineBehavior": "Auto"
    }
  }
}

I can step through the client in the debugger and see it create the serializer with the settings (which I don't currently have exposed to change). Here is what the relevant properties of the serializer say:

Serializer Settings

As far as I can tell that should parse out correctly, but it doesn't when it actually comes to it...

Can anyone tell me what I'm doing wrong here or how to correct it?


Solution

  • The DateTime from the serializer will have a DateTimeKind of DateTimeKind.Utc. Unfortunately, comparing that with another DateTime (even of type DateTimeKind.Local), will ignore the Kind and just compare them as is. If this is a problem, you can compare to DateTime.UtcNow. Alternatively, you can deserialize as DateTimeOffset, which will take in to account the Offset when making comparisons.