882

Given:

DateTime.UtcNow

How do I get a string which represents the same value in an ISO 8601-compliant format?

Note that ISO 8601 defines a number of similar formats. The specific format I am looking for is:

yyyy-MM-ddTHH:mm:ssZ
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Iain
  • 10,799
  • 6
  • 22
  • 13

18 Answers18

863

Note to readers: Several commenters have pointed out some problems in this answer (related particularly to the first suggestion). Refer to the comments section for more information.

DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz");

This gives you a date similar to 2008-09-22T13:57:31.2311892-04:00.

Another way is:

DateTime.UtcNow.ToString("o");

which gives you 2008-09-22T14:01:54.9571247Z

To get the specified format, you can use:

DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

DateTime Formatting Options

Phil
  • 6,085
  • 2
  • 39
  • 64
Wayne
  • 34,159
  • 4
  • 36
  • 48
  • 26
    These days, doing that (trying to render a UTC time with an offset, which doesn't make a lot of sense) throws an exception. So, I agree with the others that the "s" format with the invariant culture is probably more correct. FYI the formatexception's message is: "A UTC DateTime is being converted to text in a format that is only correct for local times. This can happen when calling DateTime.ToString using the 'z' format specifier, which will include a local time zone offset in the output." – Tom Lianza Nov 02 '10 at 04:59
  • 12
    I live in Australia, and for me I had to use `ToString("yyyy-MM-ddTHH:mm:ssK")` for this to work (with the jquery timeago plugin I was using). – GONeale Aug 03 '11 at 05:50
  • 2
    If you are working with Windows Live REST services, you need ToString("yyyy-MM-ddTHH:mm:ssK", System.Globalization.CultureInfo.InvariantCulture) too. – Adarsha Oct 18 '12 at 13:04
  • 7
    If you want to include the timezone offset, do this: `dt.ToString("s") + dt.ToString("zzz")` // 2013-12-05T07:19:04-08:00 – alekop Dec 06 '13 at 03:18
  • 1
    Grrrr. Time is such a complex topic. Tnx mr Fleming! – Jowen May 05 '14 at 10:06
  • 6
    The slashes (\:) cause issues with the string... put in an @ character to use a string literal instead. – Gigi Aug 29 '14 at 12:50
  • 1
    Interestingly, the link for "DateTime Formatting Options" doesn't include "o" in its list of string formatters. Strange. – core Oct 27 '15 at 20:52
  • 7
    @core: that's one of the standard Formats, which is different from the custom Formats linked: https://msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspx – Wayne Oct 29 '15 at 17:59
  • 2
    Please put the simplest, least code, least likely to be messed up option of `"o"` first. – binki Feb 15 '18 at 17:20
  • 2
    I think it's worth noting that when formatting from `DateTimeOffset`, the `ToString("o")` format is different than `DateTime`. `dateTime.ToString("o")` => as above, ending in `Z`. `dateTimeOffset.ToString("o")` => date ending in `+00:00`. So coming from `DateTimeOffset` you want to use `dateTimeOffset.UtcDateTime.ToString("o");`. – Matt Scully Aug 29 '18 at 15:02
  • 1
    The first option you give is actually very wrong: You're adding a time zone offset to a UTC time. The only way that option could be correct is if you were using `DateTime.Now` instead of `DateTime.UtcNow`. The second option with the `"o"` format is correct and less error-prone. – Phil Jan 10 '19 at 10:41
  • It's not wrong to add Z or +0:00 to an UTC time. If the only information about the time is a string then it's a legit way to inform that it's an UTC time. – qwlice Jun 12 '19 at 08:53
  • `+00:00` as an offset to represent UTC is valid according to [ISO8601 specs](http://www.loc.gov/standards/datetime/iso-tc154-wg5_n0038_iso_wd_8601-1_2016-02-16.pdf) (section 3.4.2): "represents a plus sign [+] if in combination with the following element a positive value or **zero** needs to be represented. So simply use the `o` format with both `DateTime` and `DateTimeOffset`. – Saeb Amini Dec 04 '19 at 01:12
  • ISO 8601 are Gregorian, so the first part of your answer, `ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz")`, will produce wrong results in non-Gregorian cultures such as `new CultureInfo("ar-SA")`. Demo fiddle here: https://dotnetfiddle.net/RNvaPr. You need to pass an additional parameter `CultureInfo.InvariantCulture`. Since this is the top-voted answer, I'd like to make the correction. – dbc Mar 08 '21 at 14:35
  • From another SO page (which I don't remember), I would say `.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK", CultureInfo.InvariantCulture)` is the most complete way. Referring to [Official Docs](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings) the 'K' formatter works the best. If you call it on `DateTime.Now` (local time), it shows the offset to the UTC, and if you call it on `DateTime.UtcNow` (reference time), it appends the character 'Z' as an indicator. – Siavash Mortazavi Mar 26 '21 at 16:44
426

DateTime.UtcNow.ToString("s", System.Globalization.CultureInfo.InvariantCulture) should give you what you are looking for as the "s" format specifier is described as a sortable date/time pattern; conforms to ISO 8601.

EDIT: To get the additional Z at the end as the OP requires, use "o" instead of "s".

Neo
  • 3,225
  • 5
  • 40
  • 60
Simon Wilson
  • 8,879
  • 3
  • 26
  • 24
  • 41
    I believe this is the correct answer. There is no point in explicitly defining the yyyy-MM-etc if Microsoft already implemented ISO 8601. Iain's response was right, too, but you should always specify the InvariantCulture (or any other CultureInfo) for multiple reasons (i.e. never assume .NET should just assume). You can also use: `DateTime.UtcNow.ToString(CultureInfo.InvariantCulture.DateTimeFormat.SortableDateTimePattern);` However, since all of these exclude the time zone, etc., you might have no choice but to use the explicit formatter, i.e. `"yyyy-MM-ddTHH:mm:ss.fffZ"` – Jon Davis Aug 20 '10 at 20:07
  • 25
    While it conforms, it leaves out the timezone, `Z`, looking like this: `DateTime.UtcNow.ToString(c, CultureInfo.InvariantCulture)) => 2012-06-26T11:55:36` and there's no millisecond resolution that is very nice to have since computers do a fair number of ticks per second. – Henrik Jun 26 '12 at 12:05
  • 10
    With `o` you get `2012-06-26T11:55:36.1007668Z` meaning `36.1007668` seconds, so you get resolution down to `1/10^7` of a second. From ISO8601:2004 `If a decimal fraction is included, lower order time elements (if any) shall be omitted and the decimal fraction shall be divided from the integer part by the decimal sign [...] the comma (,) or full stop (.)` – Henrik Jun 26 '12 at 12:11
  • 29
    @stimpy77 Specifying CultureInfo for `"s"` makes no sense because: [“"O" (or "o"), "R" (or "r"), "s", and "u". These strings correspond to custom format strings defined by the invariant culture. They produce string representations of date and time values that are intended to be identical across cultures.”](http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo%28v=vs.110%29) – binki Dec 05 '13 at 18:44
  • @binki Are you sure `s` is invariant? From your link ([jump to format strings](http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo%28v=vs.110%29#properties)) it says `s` uses [`SortableDateTimePattern`](http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo.sortabledatetimepattern(v=vs.110)), which states "property defines the **culture-specific** format of date strings that are...supplied the "s" standard format string." Same for `R`. `U` and `O` on the other hand are invariant. – drzaus Apr 09 '14 at 13:45
  • @drzaus, `DateTimeFormatInfo` is a `sealed` class. In the enumeration of [types of format strings](http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo%28v=vs.110%29#properties), `SortableDateTimePattern` is not listed as “Properties that define culture-sensitive result strings.” Instead, further down in the table, `"s"` is documented “to define a result string that conforms to the ISO 8601 standard”. ISO 8601 has no localization considerations AFAIK. Just hoping that the class library actually does as its documentation says ;-). – binki Apr 10 '14 at 13:30
  • 2
    @binki - now I'm very confused. According to the [documentation I linked earlier for SortableDateTimePattern](http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo.sortabledatetimepattern(v=vs.110)), it says it should be culture specific. HOWEVER, it seems to be contradicted by its own examples (since they all look the same); try `DateTime.Now.ToString("s", new CultureInfo(myCulture))`. – drzaus Apr 10 '14 at 21:02
  • @drzaus, I find it confusing too. But I think it just means that you can be using any `CultureInfo` object, even something that isn’t `InvariantCulture`, and get the same results. I.e., ISO8601 gets you the same string regardless of culture. Then you don’t have to bother with `InvariantCulture`(?). But it just does seem out of place in a class where most other members *should* differ depending on the culture. – binki Apr 10 '14 at 21:09
  • `"o"` is superior. – binki Feb 15 '18 at 17:26
  • 2
    Yes, `"s"` is confusing. Definition vs. implementation? "**The SortableDateTimePattern property defines the culture-specific format of date strings**", but it is implemented as a defined standard that does not change per culture: "The format string returned by the SortableDateTimePattern property reflects **a defined standard (ISO 8601)** [...] Therefore, it is **always the same, regardless of the culture**." Quotes are from the documentation, https://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo.sortabledatetimepattern.aspx. – Timo May 14 '18 at 15:27
  • Whether you choose [`"o"`](https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip) or [`"s"`](https://docs.microsoft.com/en-us/dotnet/standard/base-types/standard-date-and-time-format-strings#Sortable) depends on your usage and it's worth reading the documentation on both (links herein). But in short, `"o"` is useful where you want to preserve the `Kind` property when parsing the result in reverse (hence "round-trip"). This is useful when storing the value in a database for later retrieval for example. – Neo Aug 27 '19 at 23:30
100
DateTime.UtcNow.ToString("s")

Returns something like 2008-04-10T06:30:00

UtcNow obviously returns a UTC time so there is no harm in:

string.Concat(DateTime.UtcNow.ToString("s"), "Z")
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Iain
  • 10,799
  • 6
  • 22
  • 13
  • 13
    Just out of interest: Why string.Concat() rather than '+'? – Daniel Fortunov Sep 22 '08 at 14:53
  • 2
    Habbit, is there a difference? – Iain Sep 23 '08 at 13:26
  • 85
    @KoenZomers: I don't think that's correct. I think `a + b` compiles to the same intermediate code as `string.Concat(a, b)` (assuming that a and b are strings, of course) so there is no difference in performance or memory consumption. – Mark Byers Jan 25 '12 at 13:54
  • 79
    Yes, Mark is correct. Koen, you have just fallen into the trap of an absurdly premature micro-optimisation, even if you are correct. – Noldorin Feb 14 '12 at 14:03
  • 1
    Why not use `o` instead to get the timezone? Granted, this will include microseconds like `2014-04-09T09:38:29.7562176-04:00` (from `DateTime.Now`) or `2014-04-09T13:38:29.7562176Z` (`UtcNow`). [Compare format options](http://msdn.microsoft.com/en-us/library/system.globalization.datetimeformatinfo%28v=vs.110%29#properties) – drzaus Apr 09 '14 at 13:50
  • 2
    @MarkByers, that's not correct. @Noldorin, it's not premature if this is called in a loop that executes thousands of times, or if it's a web application running this code on each request. IN my opinion `string.Concat` should *always* be used for intensive string concatenation (by intensive, I mean used regularly by an application, not a user - again I'm thinking of the web). @KoenZomers `string.Format` is actually the slowest way to do it. See halfway down this page for benchmarks: http://www.dotnetperls.com/string-concat – greg84 May 22 '14 at 21:50
  • 8
    @greg84: Well, you are not entirely right. Look at this post by Microsoft architect Rico Mariani: http://blogs.msdn.com/b/ricom/archive/2003/12/15/43628.aspx - he says a + b does compile to concat + there's some more information about proper usage of StringBuilder. – mrówa Sep 17 '14 at 15:55
  • 1
    From the links mentioned above, there is no difference when concatenating two strings. + is just shorter in source code. `string.Concat` gets a performance effect when concatenating at least 3 strings in one step. – Stéphane Gourichon Sep 23 '16 at 15:58
  • 1
    Actually string.Concat hurts you because the compiler optimizes + but not String.Concat. http://stackoverflow.com/a/8864900/86973 – PRMan Jan 19 '17 at 19:33
  • @PRMan: That's only true in this case because they are string literals (as in, they could be converted to constants). It recognizes this so it just precombines them during compilation into one string. If you had var someString = "World"; var concatd = "Hello" + someString + "!"; then it's NOT going to optimize it into "Hello World!". If 'someString' was a constant then it probably will. – Brent Rittenhouse Dec 15 '17 at 21:35
  • 1
    This is more complicated than `"o"`. Also, the difference with using `string.Concat()` is that you have more code on the screen when it isn’t needed (i.e., your strings aren’t coming from an `IEnumerable` or dynamically built array), making it less readable. – binki Feb 15 '18 at 17:19
  • err... who said it has to be fast instead of human readable? Is it optimized for the time spent to write 'String.Concat(....' - which is priceless :D:D – d.popov Jul 18 '18 at 15:35
  • You know the answer is old when the example contains a date that's over 10 years ago. Hey it still works flawlessly! – Gaspa79 Aug 02 '18 at 20:39
  • @BrentRittenhouse, Actually "literal" + variable + "literal2" is exactly the same as string.Concat. And if you have enough + signs, it actually optimizes it into a string array that is then sent to string.Concat(string[]). – PRMan Nov 20 '18 at 23:18
44

Use:

private void TimeFormats()
{
    DateTime localTime = DateTime.Now;
    DateTime utcTime = DateTime.UtcNow;
    DateTimeOffset localTimeAndOffset = new DateTimeOffset(localTime, TimeZoneInfo.Local.GetUtcOffset(localTime));

    //UTC
    string strUtcTime_o = utcTime.ToString("o");
    string strUtcTime_s = utcTime.ToString("s");
    string strUtcTime_custom = utcTime.ToString("yyyy-MM-ddTHH:mm:ssK");

    //Local
    string strLocalTimeAndOffset_o = localTimeAndOffset.ToString("o");
    string strLocalTimeAndOffset_s = localTimeAndOffset.ToString("s");
    string strLocalTimeAndOffset_custom = utcTime.ToString("yyyy-MM-ddTHH:mm:ssK");

    //Output
    Response.Write("<br/>UTC<br/>");
    Response.Write("strUtcTime_o: " + strUtcTime_o + "<br/>");
    Response.Write("strUtcTime_s: " + strUtcTime_s + "<br/>");
    Response.Write("strUtcTime_custom: " + strUtcTime_custom + "<br/>");

    Response.Write("<br/>Local Time<br/>");
    Response.Write("strLocalTimeAndOffset_o: " + strLocalTimeAndOffset_o + "<br/>");
    Response.Write("strLocalTimeAndOffset_s: " + strLocalTimeAndOffset_s + "<br/>");
    Response.Write("strLocalTimeAndOffset_custom: " + strLocalTimeAndOffset_custom + "<br/>");

}

OUTPUT

UTC
    strUtcTime_o: 2012-09-17T22:02:51.4021600Z
    strUtcTime_s: 2012-09-17T22:02:51
    strUtcTime_custom: 2012-09-17T22:02:51Z

Local Time
    strLocalTimeAndOffset_o: 2012-09-17T15:02:51.4021600-07:00
    strLocalTimeAndOffset_s: 2012-09-17T15:02:51
    strLocalTimeAndOffset_custom: 2012-09-17T22:02:51Z

Sources:

Kolappan N
  • 2,178
  • 2
  • 26
  • 31
Don
  • 669
  • 6
  • 3
  • 2
    seems you are a victim of copying at local custom ;-) `string strLocalTimeAndOffset_custom = localTimeAndOffset.ToString("yyyy-MM-ddTHH:mm:ssK");` would result in: `strLocalTimeAndOffset_custom: 2012-09-17T22:02:51-07:00` – Holly Apr 29 '15 at 09:42
40
System.DateTime.UtcNow.ToString("o")

=>

val it : string = "2013-10-13T13:03:50.2950037Z"
Henrik
  • 9,303
  • 4
  • 49
  • 83
  • Agreed this is the only way to be absolutely sure that you have an unambiguous date/time across any timezone – Matt Wilko Sep 01 '15 at 15:54
28

You have a few options including the "Round-trip ("O") format specifier".

var date1 = new DateTime(2008, 3, 1, 7, 0, 0);
Console.WriteLine(date1.ToString("O"));
Console.WriteLine(date1.ToString("s", System.Globalization.CultureInfo.InvariantCulture));

Output

2008-03-01T07:00:00.0000000
2008-03-01T07:00:00

However, DateTime + TimeZone may present other problems as described in the blog post DateTime and DateTimeOffset in .NET: Good practices and common pitfalls:

DateTime has countless traps in it that are designed to give your code bugs:

1.- DateTime values with DateTimeKind.Unspecified are bad news.

2.- DateTime doesn't care about UTC/Local when doing comparisons.

3.- DateTime values are not aware of standard format strings.

4.- Parsing a string that has a UTC marker with DateTime does not guarantee a UTC time.

Alex Nolasco
  • 17,012
  • 9
  • 71
  • 76
  • 4
    ISO8601 is used in strava for one. However please use:StartTime.ToString("yyyy-MM-ddTHH:mm:ssZ") rather than ToString("o") which adds milliseconds etc. – peterincumbria Mar 13 '16 at 15:42
  • 4
    For me, "yyyy-MM-dd-THH:mm:ssZ" literally outputted "Z" at the end of my string instead of a timezone marker, which did not do what I wanted. ToString("o") actually did what I needed, much easier and shorter. – Blair Connolly Jan 18 '17 at 21:45
  • @BlairConnolly You were right. The "z" format specifier [should have been lowercase](https://docs.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings#zSpecifier). As indicated [here](https://lucene.apache.org/solr/guide/8_5/working-with-dates.html#date-formatting), the capital "Z" is only valid when your date is actually in UTC. – jpaugh Jun 24 '20 at 15:02
26

Surprised that no one suggested it:

System.DateTime.UtcNow.ToString("u").Replace(' ','T')
# Using PowerShell Core to demo

# Lowercase "u" format
[System.DateTime]::UtcNow.ToString("u")
> 2020-02-06 01:00:32Z

# Lowercase "u" format with replacement
[System.DateTime]::UtcNow.ToString("u").Replace(' ','T')
> 2020-02-06T01:00:32Z

The UniversalSortableDateTimePattern gets you almost all the way to what you want (which is more an RFC 3339 representation).


Added: I decided to use the benchmarks that were in answer https://stackoverflow.com/a/43793679/653058 to compare how this performs.

tl:dr; it's at the expensive end but still just a little over half a millisecond on my crappy old laptop :-)

Implementation:

[Benchmark]
public string ReplaceU()
{
   var text = dateTime.ToUniversalTime().ToString("u").Replace(' ', 'T');
   return text;
}

Results:

// * Summary *

BenchmarkDotNet=v0.11.5, OS=Windows 10.0.19002
Intel Xeon CPU E3-1245 v3 3.40GHz, 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100
  [Host]     : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
  DefaultJob : .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT


|               Method |     Mean |     Error |    StdDev |
|--------------------- |---------:|----------:|----------:|
|           CustomDev1 | 562.4 ns | 11.135 ns | 10.936 ns |
|           CustomDev2 | 525.3 ns |  3.322 ns |  3.107 ns |
|     CustomDev2WithMS | 609.9 ns |  9.427 ns |  8.356 ns |
|              FormatO | 356.6 ns |  6.008 ns |  5.620 ns |
|              FormatS | 589.3 ns |  7.012 ns |  6.216 ns |
|       FormatS_Verify | 599.8 ns | 12.054 ns | 11.275 ns |
|        CustomFormatK | 549.3 ns |  4.911 ns |  4.594 ns |
| CustomFormatK_Verify | 539.9 ns |  2.917 ns |  2.436 ns |
|             ReplaceU | 615.5 ns | 12.313 ns | 11.517 ns |

// * Hints *
Outliers
  BenchmarkDateTimeFormat.CustomDev2WithMS: Default     -> 1 outlier  was  removed (668.16 ns)
  BenchmarkDateTimeFormat.FormatS: Default              -> 1 outlier  was  removed (621.28 ns)
  BenchmarkDateTimeFormat.CustomFormatK: Default        -> 1 outlier  was  detected (542.55 ns)
  BenchmarkDateTimeFormat.CustomFormatK_Verify: Default -> 2 outliers were removed (557.07 ns, 560.95 ns)

// * Legends *
  Mean   : Arithmetic mean of all measurements
  Error  : Half of 99.9% confidence interval
  StdDev : Standard deviation of all measurements
  1 ns   : 1 Nanosecond (0.000000001 sec)

// ***** BenchmarkRunner: End *****

rburte
  • 517
  • 4
  • 9
  • 2
    The accepted answer of "o" does work, but it gives an annoying amount of precision (geez .XXXXXXX seconds) whereas I prefer this since it stops at seconds. – jhocking Jun 06 '19 at 01:39
  • 3
    Also that doc claims "u" is ISO 8601, but what's with the space instead of T? get it together microsoft – jhocking Jun 06 '19 at 01:45
  • @jhocking https://en.wikipedia.org/wiki/ISO_8601#cite_note-30 ISO 8601 is relatively permissive if you read through it... – rburte Jun 06 '19 at 21:27
25

You can get the "Z" (ISO 8601 UTC) with the next code:

Dim tmpDate As DateTime = New DateTime(Now.Ticks, DateTimeKind.Utc)
Dim res as String = tmpDate.toString("o") '2009-06-15T13:45:30.0000000Z


Here is why:

The ISO 8601 have some different formats:

DateTimeKind.Local

2009-06-15T13:45:30.0000000-07:00

DateTimeKind.Utc

2009-06-15T13:45:30.0000000Z

DateTimeKind.Unspecified

2009-06-15T13:45:30.0000000


.NET provides us with an enum with those options:

'2009-06-15T13:45:30.0000000-07:00
Dim strTmp1 As String = New DateTime(Now.Ticks, DateTimeKind.Local).ToString("o")

'2009-06-15T13:45:30.0000000Z
Dim strTmp2 As String = New DateTime(Now.Ticks, DateTimeKind.Utc).ToString("o")

'2009-06-15T13:45:30.0000000
Dim strTmp3 As String = New DateTime(Now.Ticks, DateTimeKind.Unspecified).ToString("o")

Note: If you apply the Visual Studio 2008 "watch utility" to the toString("o") part you may get different results, I don't know if it's a bug, but in this case you have better results using a String variable if you're debugging.

Source: Standard Date and Time Format Strings (MSDN)

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Oaxas
  • 369
  • 4
  • 7
16

Most of these answers have milliseconds / microseconds which clearly isn't supported by ISO 8601. The correct answer would be:

System.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssK");
// or
System.DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK");

References:

Kolappan N
  • 2,178
  • 2
  • 26
  • 31
Justin Turner
  • 409
  • 3
  • 6
  • 16
    Read your own Wikipedia link under "Times". It mentions "Decimal fractions", meaning ISO 8601 supports both milliseconds and microseconds (but communicating parties may limit number of decimal places accepted). – Søren Boisen May 06 '15 at 12:56
16

I would just use XmlConvert:

XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.RoundtripKind);

It will automatically preserve the time zone.

Kolappan N
  • 2,178
  • 2
  • 26
  • 31
Sumrak
  • 3,700
  • 7
  • 28
  • 36
  • I went ahead and added an extension method. public static class DateTimeExtensions { public static string ToIsoFormat(this DateTime dateTime) { return XmlConvert.ToString(dateTime, XmlDateTimeSerializationMode.RoundtripKind); } } – muruge Mar 26 '12 at 21:32
13
DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss zzz");

DateTime.Now.ToString("O");

NOTE: Depending on the conversion you are doing on your end, you will be using the first line (most like it) or the second one.

Make sure to applied format only at local time, since "zzz" is the time zone information for UTC conversion.

image

PSM
  • 181
  • 2
  • 8
  • I'm not so sure #ChrisHynes since he is asking about the suggestion I made regarding the first line of code, but if you are correct and that's the case the answer is "ReSharper" – PSM Nov 18 '18 at 03:11
9

The "s" standard format specifier represents a custom date and time format string that is defined by the DateTimeFormatInfo.SortableDateTimePattern property. The pattern reflects a defined standard (ISO 8601), and the property is read-only. Therefore, it is always the same, regardless of the culture used or the format provider supplied. The custom format string is "yyyy'-'MM'-'dd'T'HH':'mm':'ss".

When this standard format specifier is used, the formatting or parsing operation always uses the invariant culture.

– from MSDN

Rory O'Kane
  • 25,436
  • 11
  • 86
  • 123
Amal
  • 431
  • 6
  • 16
9

To convert DateTime.UtcNow to a string representation of yyyy-MM-ddTHH:mm:ssZ, you can use the ToString() method of the DateTime structure with a custom formatting string. When using custom format strings with a DateTime, it is important to remember that you need to escape your seperators using single quotes.

The following will return the string represention you wanted:

DateTime.UtcNow.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'", DateTimeFormatInfo.InvariantInfo)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Oppositional
  • 10,701
  • 6
  • 47
  • 60
9

It is interesting that custom format "yyyy-MM-ddTHH:mm:ssK" (without ms) is the quickest format method.

Also it is interesting that "S" format is slow on Classic and fast on Core...

Of course numbers are very close, between some rows difference is insignificant (tests with suffix _Verify are the same as those that are without that suffix, demonstrates results repeatability)

BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  Core   : .NET Core 4.6.25009.03, 64bit RyuJIT


               Method |  Job | Runtime |       Mean |     Error |    StdDev |     Median |        Min |        Max | Rank |  Gen 0 | Allocated |
--------------------- |----- |-------- |-----------:|----------:|----------:|-----------:|-----------:|-----------:|-----:|-------:|----------:|
           CustomDev1 |  Clr |     Clr | 1,089.0 ns | 22.179 ns | 20.746 ns | 1,079.9 ns | 1,068.9 ns | 1,133.2 ns |    8 | 0.1086 |     424 B |
           CustomDev2 |  Clr |     Clr | 1,032.3 ns | 19.897 ns | 21.289 ns | 1,024.7 ns | 1,000.3 ns | 1,072.0 ns |    7 | 0.1165 |     424 B |
     CustomDev2WithMS |  Clr |     Clr | 1,168.2 ns | 16.543 ns | 15.474 ns | 1,168.5 ns | 1,149.3 ns | 1,189.2 ns |   10 | 0.1625 |     592 B |
              FormatO |  Clr |     Clr | 1,563.7 ns | 31.244 ns | 54.721 ns | 1,532.5 ns | 1,497.8 ns | 1,703.5 ns |   14 | 0.2897 |     976 B |
              FormatS |  Clr |     Clr | 1,243.5 ns | 24.615 ns | 31.130 ns | 1,229.3 ns | 1,200.6 ns | 1,324.2 ns |   13 | 0.2865 |     984 B |
       FormatS_Verify |  Clr |     Clr | 1,217.6 ns | 11.486 ns | 10.744 ns | 1,216.2 ns | 1,205.5 ns | 1,244.3 ns |   12 | 0.2885 |     984 B |
        CustomFormatK |  Clr |     Clr |   912.2 ns | 17.915 ns | 18.398 ns |   916.6 ns |   878.3 ns |   934.1 ns |    4 | 0.0629 |     240 B |
 CustomFormatK_Verify |  Clr |     Clr |   894.0 ns |  3.877 ns |  3.626 ns |   893.8 ns |   885.1 ns |   900.0 ns |    3 | 0.0636 |     240 B |
           CustomDev1 | Core |    Core |   989.1 ns | 12.550 ns | 11.739 ns |   983.8 ns |   976.8 ns | 1,015.5 ns |    6 | 0.1101 |     423 B |
           CustomDev2 | Core |    Core |   964.3 ns | 18.826 ns | 23.809 ns |   954.1 ns |   935.5 ns | 1,015.6 ns |    5 | 0.1267 |     423 B |
     CustomDev2WithMS | Core |    Core | 1,136.0 ns | 21.914 ns | 27.714 ns | 1,138.1 ns | 1,099.9 ns | 1,200.2 ns |    9 | 0.1752 |     590 B |
              FormatO | Core |    Core | 1,201.5 ns | 16.262 ns | 15.211 ns | 1,202.3 ns | 1,178.2 ns | 1,225.5 ns |   11 | 0.0656 |     271 B |
              FormatS | Core |    Core |   993.5 ns | 19.272 ns | 24.372 ns |   999.4 ns |   954.2 ns | 1,029.5 ns |    6 | 0.0633 |     279 B |
       FormatS_Verify | Core |    Core | 1,003.1 ns | 17.577 ns | 16.442 ns | 1,009.2 ns |   976.1 ns | 1,024.3 ns |    6 | 0.0674 |     279 B |
        CustomFormatK | Core |    Core |   878.2 ns | 17.017 ns | 20.898 ns |   877.7 ns |   851.4 ns |   928.1 ns |    2 | 0.0555 |     215 B |
 CustomFormatK_Verify | Core |    Core |   863.6 ns |  3.968 ns |  3.712 ns |   863.0 ns |   858.6 ns |   870.8 ns |    1 | 0.0550 |     215 B |

Code:

    public class BenchmarkDateTimeFormat
    {
        public static DateTime dateTime = DateTime.Now;

        [Benchmark]
        public string CustomDev1()
        {
            var d = dateTime.ToUniversalTime();
            var sb = new StringBuilder(20);

            sb.Append(d.Year).Append("-");
            if (d.Month <= 9)
                sb.Append("0");
            sb.Append(d.Month).Append("-");
            if (d.Day <= 9)
                sb.Append("0");
            sb.Append(d.Day).Append("T");
            if (d.Hour <= 9)
                sb.Append("0");
            sb.Append(d.Hour).Append(":");
            if (d.Minute <= 9)
                sb.Append("0");
            sb.Append(d.Minute).Append(":");
            if (d.Second <= 9)
                sb.Append("0");
            sb.Append(d.Second).Append("Z");
            var text = sb.ToString();
            return text;
        }

        [Benchmark]
        public string CustomDev2()
        {
            var u = dateTime.ToUniversalTime();
            var sb = new StringBuilder(20);
            var y = u.Year;
            var d = u.Day;
            var M = u.Month;
            var h = u.Hour;
            var m = u.Minute;
            var s = u.Second;
            sb.Append(y).Append("-");
            if (M <= 9)
                sb.Append("0");
            sb.Append(M).Append("-");
            if (d <= 9)
                sb.Append("0");
            sb.Append(d).Append("T");
            if (h <= 9)
                sb.Append("0");
            sb.Append(h).Append(":");
            if (m <= 9)
                sb.Append("0");
            sb.Append(m).Append(":");
            if (s <= 9)
                sb.Append("0");
            sb.Append(s).Append("Z");
            var text = sb.ToString();
            return text;
        }

        [Benchmark]
        public string CustomDev2WithMS()
        {
            var u  = dateTime.ToUniversalTime();
            var sb = new StringBuilder(23);
            var y  = u.Year;
            var d  = u.Day;
            var M  = u.Month;
            var h  = u.Hour;
            var m  = u.Minute;
            var s  = u.Second;
            var ms = u.Millisecond;
            sb.Append(y).Append("-");
            if (M <= 9)
                sb.Append("0");
            sb.Append(M).Append("-");
            if (d <= 9)
                sb.Append("0");
            sb.Append(d).Append("T");
            if (h <= 9)
                sb.Append("0");
            sb.Append(h).Append(":");
            if (m <= 9)
                sb.Append("0");
            sb.Append(m).Append(":");
            if (s <= 9)
                sb.Append("0");
            sb.Append(s).Append(".");
            sb.Append(ms).Append("Z");
            var text = sb.ToString();
            return text;
        }
        [Benchmark]
        public string FormatO()
        {
            var text = dateTime.ToUniversalTime().ToString("o");
            return text;
        }
        [Benchmark]
        public string FormatS()
        {
            var text = string.Concat(dateTime.ToUniversalTime().ToString("s"),"Z");
            return text;
        }

        [Benchmark]
        public string FormatS_Verify()
        {
            var text = string.Concat(dateTime.ToUniversalTime().ToString("s"), "Z");
            return text;
        }

        [Benchmark]
        public string CustomFormatK()
        {
            var text = dateTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK");
            return text;
        }

        [Benchmark]
        public string CustomFormatK_Verify()
        {
            var text = dateTime.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssK");
            return text;
        }
    }

https://github.com/dotnet/BenchmarkDotNet was used

Roman Pokrovskij
  • 7,969
  • 14
  • 68
  • 119
5

Using Newtonsoft.Json, you can do

JsonConvert.SerializeObject(DateTime.UtcNow)

Example: https://dotnetfiddle.net/O2xFSl

blackforest-tom
  • 422
  • 4
  • 8
2

If you're developing under SharePoint 2010 or higher you can use

using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
...
string strISODate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Simon Logic
  • 150
  • 1
  • 7
  • 24
    SharePoint, when your .Net isn't Java enough. – Henrik Feb 27 '15 at 15:30
  • 21
    Using SharePoint for this is kind of like bringing a tub of jelly, a wet box of matches and 2 trapeze-walking chimpanzees to a gun fight. – nathanchere Jun 29 '15 at 08:27
  • Even in SharePoint hopefully you can use the BCL’s `.ToString("o")` or, better, `$"My complicated string {dt:o}"`. – binki Feb 15 '18 at 17:34
2

To format like 2018-06-22T13:04:16 which can be passed in the URI of an API use:

public static string FormatDateTime(DateTime dateTime)
{
    return dateTime.ToString("s", System.Globalization.CultureInfo.InvariantCulture);
}
Nick Gallimore
  • 891
  • 9
  • 25
2

As mentioned in other answer, DateTime has issues by design.

NodaTime

I suggest to use NodaTime to manage date/time values:

  • Local time, date, datetime
  • Global time
  • Time with timezone
  • Period
  • Duration

Formatting

So, to create and format ZonedDateTime you can use the following code snippet:

var instant1 = Instant.FromUtc(2020, 06, 29, 10, 15, 22);

var utcZonedDateTime = new ZonedDateTime(instant1, DateTimeZone.Utc);
utcZonedDateTime.ToString("yyyy-MM-ddTHH:mm:ss'Z'", CultureInfo.InvariantCulture);
// 2020-06-29T10:15:22Z


var instant2 = Instant.FromDateTimeUtc(new DateTime(2020, 06, 29, 10, 15, 22, DateTimeKind.Utc));

var amsterdamZonedDateTime = new ZonedDateTime(instant2, DateTimeZoneProviders.Tzdb["Europe/Amsterdam"]);
amsterdamZonedDateTime.ToString("yyyy-MM-ddTHH:mm:ss'Z'", CultureInfo.InvariantCulture);
// 2020-06-29T12:15:22Z

For me NodaTime code looks quite verbose. But types are really useful. They help to handle date/time values correctly.

Newtonsoft.Json

To use NodaTime with Newtonsoft.Json you need to add reference to NodaTime.Serialization.JsonNet NuGet package and configure JSON options.

services
    .AddMvc()
    .AddJsonOptions(options =>
    {
        var settings=options.SerializerSettings;
        settings.DateParseHandling = DateParseHandling.None;
        settings.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb);
    });
Vladimir Serykh
  • 2,891
  • 15
  • 21