30

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!

6
  • 1
    So what results are you getting? I would strongly advise explicitly specifying the invariant culture, and your .sss should be .fff, but other than that it looks okay. Commented Jun 27, 2017 at 19:27
  • 1
    Possible duplicate of Given a DateTime object, how do I get an ISO 8601 date in string format? Commented Jun 27, 2017 at 19:27
  • i've checked the 'other' listings but "yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz" give me a runtime error stating that it needs to use a Z, not a z. Didn't quite understand the error message. but as stated i'm not getting 'time'. and the format doesn't quite come back the same as the original js conversion string. Commented Jun 27, 2017 at 19:34
  • As per Max, looks like reason for missing time is i am adding Date for no reason. But i'm still off on milliseconds in reproducing the exact string once again. Commented Jun 27, 2017 at 19:48
  • an alternative to this problem would be how to get Newtonsoft not to convert the datetime string to a date time which it seems to do automatically. so i can use the original string. Commented Jun 27, 2017 at 19:53

2 Answers 2

84

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Tx! Date, max pointed out that it not needed. as far the sssZ, i got that from the toISOString js string spec. Tx for full explanation.
When it comes to formatting specifiers (tokens, whatever you call them), almost every language and library is going to be slightly different. Always check the docs.
.ToString("o") is really the simplest way to go, and you don't need to specify the culture there either. According to the Microsoft docs: "Because the "O" or "o" standard format specifier conforms to an international standard, the formatting or parsing operation that uses the specifier always uses the invariant culture and the Gregorian calendar."
5

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

6 Comments

when i try theTime.Date.ToString("o") i get "2017-06-26T00:00:00.0000000Z"... oh wait.. when i remove DATE part.. i get the time.
ok, getting somewhere with this. But how can i get the same? meaning i need the same as the original string! i tried strTime = theTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.sssZ"); but it's missing the milli second like you stated... i need the original string to use in a hash, so i need exactly the same.
Also the issue is that the original DATETIME UTC as listed in the beginning of the issue is what included milliseconds. i only replicated what that was in c# datetime var.
@user1161137 I gave the answer. Your date time has millisecond accuracy, and I print it back to you with millisecond accuracy. The "07" in "0700000Z" in the timestamp under Solution is your "70" in the milliseconds column.
theTime.ToString("o") doesn't return what i asked.. which is exactly what it came in as. the "yyyy-MM-dd'T'HH:mm:ss.fffK" does return it exactly... which Matt identified and explained..
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.