Passing DateTimeOffset as WebAPI query string

The problem is the + (plus) char in the offset part, we should encode that.

If the offset is - (minus) no need to encode

Encoded value of +(plus) is %2B So the 2021-05-05T18:00:00+05:00 should be passed as 2021-05-05T18:00:00%2B05:00

http://localhost:1234/api/values/1?date=2021-05-05T18:00:00%2B05:00

if the offset is - (minus) then

http://localhost:1234/api/values/1?date=2021-05-05T18:00:00-05:00

The problem is being described exactly by the 400 response message, although it could have been more clear. The route, as defined by the attribute, only expects a parameter id, but the Delete method expects another parameter called date.

If you want to provide this value using the query string, you'll need to make that parameter nullable, by using "DateTimeOffset?", which would also transform it into an optional parameter. If the date is a required field, consider adding it to the route, like:

[Route("api/values/{id}/{date}")]

OK, ignore what I typed above, it's just a formatting problem. Web API has trouble figuring out the culture needed to parse the given value, but if you try to pass on DateTimeOffset using a JSON format in the query string, like 2014-05-06T22:24:55Z, that should work.


Answer

To send a DateTimeOffset to your API, format it like this after converting it to UTC:

2017-04-17T05:04:18.070Z

The complete API URL will look like this:

http://localhost:1234/api/values/1?date=2017-04-17T05:45:18.070Z

It’s important to first convert the DateTimeOffset to UTC, because, as @OffHeGoes points out in the comments, the Z at the end of the string indicates Zulu Time (more commonly known as UTC).

Code

You can use .ToUniversalTime().ToString(yyyy-MM-ddTHH:mm:ss.fffZ) to parse the DateTimeOffset.

To ensure your DateTimeOffset is formatted using the correct timezone always use .ToUniversalTime() to first convert the DateTimeOffset value to UTC, because the Z at the end of the string indicates UTC, aka "Zulu Time".

DateTimeOffset currentTime = DateTimeOffset.UtcNow;
string dateTimeOffsetAsAPIParameter = currentDateTimeOffset.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
string apiUrl = string.Format("http://localhost:1234/api/values/1?date={0}", dateTimeOffsetAsAPIParameter);

The current accepted answer throws away the time zone information, which in some cases is important. The following maintains the time zone and doesn't lose any precision. It also keeps your code succinct when building a query string.

public static string UrlEncode(this DateTimeOffset dateTimeOffset)
{
     return HttpUtility.UrlEncode(dateTimeOffset.ToString("o"));
}