Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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!

like image 300
user1161137 Avatar asked Jun 27 '17 19:06

user1161137


2 Answers

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.

like image 97
Matt Johnson-Pint Avatar answered Oct 02 '22 12:10

Matt Johnson-Pint


The problem is that you are losing accuracy by using a cultural standard such as UTC or UniversalTime. If you just print your DateTime:

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

6/26/2017 8:45:00 PM

You can read more about this issue here.

The solution is to not use any "culture". (For example, UniversalTime, or UtcNow). These cultural standards never include milliseconds ... because there is no culture where people really care all that often about milliseconds.

Solution:

var newTime = theTime.ToString("o");
Console.WriteLine(newTime);

2017-06-26T20:45:00.0700000Z

like image 45
Max von Hippel Avatar answered Oct 02 '22 12:10

Max von Hippel