Search code examples
c#datetimeiso8601

c# convert datetime object to iso 8601 string


I'm trying to convert a DateTime object to a ISO8601 string but keep getting wrong results. I've looked around on stackoverflow, but couldn't find the right solution.

I start with a date time string of "2017-06-26T20:45:00.070Z" which deserialized by newtonsoft from json and converted to a DateTime object in C# equivalent to :

var theTime = new DateTime(2017, 6, 26, 20, 45, 00, 70, DateTimeKind.Utc);

Now i need the convert that time back to it's original UTC format string to use it in another algorithm, but every conversion i try doesn't return it to that original string. Not sure what i'm doing wrong.

i've tried:

var newTime = theTime.UtcNow.ToString("o");
// returns "2017-06-26T00:00:00.0000000Z"

var newTime2 = theTime.Date.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.sssZ");
// returns "2017-06-26T00:00:00.00Z"

what am i doing wrong? I want the equivalent to what js will do using toISOString() which is what i have listed in the newTime2 date time format, but it's not showing times either.

thanks!


Solution

  • Observe:

    // Your input
    DateTime dt = new DateTime(2017, 6, 26, 20, 45, 0, 70, DateTimeKind.Utc);
    
    // ISO8601 with 7 decimal places
    string s1 = dt.ToString("o", CultureInfo.InvariantCulture);
    //=> "2017-06-26T20:45:00.0700000Z"
    
    // ISO8601 with 3 decimal places
    string s2 = dt.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture);
    //=> "2017-06-26T20:45:00.070Z"
    

    A few things:

    • Don't use Z in the format string. That's not a valid format specifier, so it is treated as just a character to output. It will be in every string, regardless of .Kind setting of the input datetime.

    • With DateTime, use K - which properly conveys the .Kind by appending a Z to the output for DateTimeKind.Utc, or an offset from UTC for DateTimeKind.Local, or nothing at all for DateTimeKind.Unspecified.

    • Though T will output as a character because it's not a valid format specifier, I suggest always being explicit about those things, so prefer 'T'.

    • Using fff will always give you back three decimals (milliseconds). If you want the decimals omitted when zero, use FFF instead. Your use of sss is not valid.

    • Passing CultureInfo.InvariantCulture is a good practice, as it helps you avoid problems where the current culture might use a different calendar system. For example ar-SA uses the UmAlQuraCalendar, rather than the proleptic Gregorian calendar required by ISO 8601.

    • In your code you tried, you had called theTime.UtcNow - that won't compile. UtcNow is a static property, not an instance property.

    • Also in your code you called theTime.Date.ToUniveralTime() - Don't do that either. .Date will set the time components to zero, and .ToUniversalTime() will have no effect since the input value already has DateTimeKind.Utc.