3

I have a swagger generated client. Surprisingly the API expects all date parameters to be exactly in UTC+02:00 and cant handle just the time zone information provided. Now whatever I do, I can't get the timezone bit correct (+02:00). The following code always prints +01:00 regardless of the time zone I provide.

TimeZoneInfo.ConvertTime(new DateTime(2020, 1, 8, 0, 0, 0, DateTimeKind.Utc), TimeZoneInfo.FindSystemTimeZoneById("Asia/Magadan")).ToString("zzz")

As I mentioned earlier this is a generated client over which I have no control. So I can not mess with the json serializer. API:

List<GLJournalEntry> Create (PostGLJournalEntriesDTO body, string idempotencyKey = null);

DTO:

        public PostGLJournalEntriesDTO(List<GLAccountAmount> credits = default(List<GLAccountAmount>), List<GLAccountAmount> debits = default(List<GLAccountAmount>), string branchId = default(string), DateTime? date = default(DateTime?), string notes = default(string), string transactionId = default(string))
        {
            this.Credits = credits;
            this.Debits = debits;
            this.BranchId = branchId;
            this.Date = date;
            this.Notes = notes;
            this.TransactionId = transactionId;
        }

JSON-Model:

"PostGLJournalEntriesDTO":{
         "type":"object",
         "properties":{
              "date":{
               "type":"string",
               "format":"date-time",
               "example":"2016-09-06T13:37:50+03:00",
               "description":"Date/time stamp when the entries were recorded (Booking Date)"
            
},
            "branchId":{
               "type":"string",
               "description":"The id of the assigned branch for the journal entries"
            
},
            "notes":{
               "type":"string",
               "description":"Optional notes entered by the user when they performed the journal entry log"
            
},
            "credits":{               "type":"array",
               "description":"The list of GL Accounts to be credited with the corresponding amounts",
               "items":{
                  "$ref":"#/definitions/GLAccountAmount"
               
}
            
},
            "debits":{               "type":"array",
               "description":"The list of GL Accounts to be debited with the corresponding amounts",
               "items":{
                  "$ref":"#/definitions/GLAccountAmount"
               
}
            
},
            "transactionId":{
               "type":"string",
               "description":"An id for the transaction. Not unique. Will be auto generated if not provided."
            
}
         
},
         "description":"The representation of a payload for creating GL Journal Entries"
      
}

An id for the transaction. Not unique. Will be auto generated if not provided. }

When I invoke the API I get an exception:

{"errors":[{"errorCode":4,"errorSource":"Invalid date offset for value 2020-01-08T23:00:00+01:00 of date org offset is +02:00","errorReason":"INVALID_PARAMETERS"}]}

If I curl the api using 2020-01-08T23:00:00+02:00, everything works.

It is pretty clear that this is a bug on their end (as even the example states a date with +03:00). However I cant wait for them fixing this for me and I need to find a workaround (at least an intermediate one). The generated client uses RestSharp to serialize out the json (Newtonsoft.Json.JsonConvert).

KIC
  • 5,887
  • 7
  • 58
  • 98
  • sorry not python, maybe "datetime" is a bad tag since it also is a python module, I removed it – KIC Jul 21 '20 at 12:35
  • @funie200 this is not a python question – Liam Jul 21 '20 at 12:38
  • Does this answer your question? [Convert UTC/GMT time to local time](https://stackoverflow.com/questions/179940/convert-utc-gmt-time-to-local-time) – Liam Jul 21 '20 at 12:41
  • @Liam thx, not really. The mentioned question is about local time and nothing about the string representation of the time offset. – KIC Jul 21 '20 at 12:43
  • Are you sure, that timezone is correct? There is no `Asia/Magadan` timezone in my end. Which OS version are you using? – Pavel Anikhouski Jul 21 '20 at 12:47
  • 1
    Linux, yes this is another .net feature I like, the TimeZone.Id's are different on each OS – KIC Jul 21 '20 at 12:54
  • 1
    @KIC the question is unclear. JSON doesn't even have dates. Using ISO8601 to store dates is a *convention*. Your dates either contain an offset or they don't. You can serialise any form you want. Which one do you actually want? – Panagiotis Kanavos Jul 21 '20 at 12:58
  • As for `Asia/Magadan` that's the IANA timezone name, not an offset. That name is *not* part of the ISO8601 format, so *most* JSON clients wouldn't recognise a string ending with an IANA timezone name. Is your *real* question how to generate an ISO8601 string when you have the IANA tz name perhaps? – Panagiotis Kanavos Jul 21 '20 at 13:03
  • Post the *schema*, not the generated DTOs. Also post your own code - dates are serialised by JSON.NET or System.Text.Json. If you want to change how a property is serialised you need to use the appropriate attributes, or use a custom converter – Panagiotis Kanavos Jul 21 '20 at 13:24
  • I have added the json relevant information, my own code does not add any value I literaly just do `api.Create (new PostGLJournalEntriesDTO(..., date=aDateIhaveGotFormAnotherApi, ...)`. The generated client uses RestSharp internally. – KIC Jul 22 '20 at 06:30

1 Answers1

3

Avoid DateTime whenever possible and use DateTimeOffset instead:

var sourceOffset = TimeSpan.Zero; // UTC
var source = new DateTimeOffset(2020, 1, 8, 0, 0, 0, sourceOffset);

The rest of your code doesn't change:

var timezone = TimeZoneInfo.FindSystemTimeZoneById("Asia/Magadan");
var timezoneStr = TimeZoneInfo.ConvertTime(source, timezone).ToString("zzz");

This will give +11:00 (which is correct for Magadan Time). If you need to convert from Magadan Time to UTC+2, you'd need to change the offset and target time zones accordingly.


As an example, here's how to go to UTC+2 as currently observed by the Africa/Cairo time zone:

var utcPlus2 = TimeZoneInfo.FindSystemTimeZoneById("Africa/Cairo");
var converted = TimeZoneInfo.ConvertTime(target, utcPlus2);
var dateTime = converted.DateTime;

Now, the dateTime value will be 1/8/2020 2:00:00 AM - which is correct, since 1/8/2020 0:00:00 AM UTC is 1/8/2020 2:00:00 AM UTC+2. Note that while you will not be able to get the correct time zone from the DateTime instance, the time value itself is correct.

sunside
  • 8,069
  • 9
  • 51
  • 74
  • Thx! Sadly while this works fine, the client only accepts `DateTime` and if I do `time.DateTime` I run into the same problem again. Looks like I am trapped ... – KIC Jul 21 '20 at 12:53
  • 1
    @KIC JSON doesn't even have dates, so that statement doesn't make much sense - there's no `only accepts DateTime`. There's a *convention* that strings in the ISO8601 format are treated as dates. Either your dates are serialised without an offset, or they contain an offset. Which one is it? If you require an offset, which one is it? You can convert any DateTime or DateTimeOffset to another one. – Panagiotis Kanavos Jul 21 '20 at 12:57
  • A generated swagger client generates all the DTOs and takes care of the conversion inside. You can not pass a json to the api but DTOs. Those DTO are build on DateTime objects. PS I am not the generator of the client, I simply get the assembly. – KIC Jul 21 '20 at 13:11
  • Off topic by a large margin, but apart from .NET's extra flavor with `DateTime` and non-standardized zone names, I always have to think of [this video](https://www.youtube.com/watch?v=-5wpm-gesOY). – sunside Jul 21 '20 at 13:11
  • @KIC you misunderstand what JSON and Swagger do. There's no `DateTime` in JSON or Swagger. What you see is *ONLY* a generated DTO, not even the Swagger/JSON schema itself. You can modify it easily and the server will understand the new value - unless they explicitly specify they accept **local time** with an implied `+2:00` offset. You still haven't given an example of what's expected though, you describe stuff generated from that expectation. The DTO isn't the JSON schema itself – Panagiotis Kanavos Jul 21 '20 at 13:14
  • > unless they explicitly specify they accept local time with an implied +2:00 offset. exactly this is the case. They exactly expect the string representation to end with +02:00. I have added the DTO and API interface method. – KIC Jul 21 '20 at 13:17
  • @KIC Swagger [doesn't allow that](https://swagger.io/docs/specification/data-models/data-types/) to begin with. It allows the `date` or `date-time` string formats. `date-time` follows [the RFC3339](https://tools.ietf.org/html/rfc3339#section-5.6) format with includes the offset. – Panagiotis Kanavos Jul 21 '20 at 13:22