Given a DateTime object, how do I get an ISO 8601 date in string format?
Asked Answered
S

19

1027

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
Stander answered 22/9, 2008 at 13:56 Comment(0)
H
1011

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.

// Do not use this
DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz", CultureInfo.InvariantCulture);

Using custom date-time formatting, this gives you a date similar to
2008-09-22T13:57:31.2311892-04:00.

Another way is:

// Prefer this, to avoid having to manually define a framework-provided format
DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture);

which uses the standard "round-trip" style (ISO 8601) to give you
2008-09-22T14:01:54.9571247Z.

To get the specified format, you can use:

DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture)
Hipparchus answered 22/9, 2008 at 14:0 Comment(16)
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."Prepossess
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).Breastpin
If you are working with Windows Live REST services, you need ToString("yyyy-MM-ddTHH:mm:ssK", System.Globalization.CultureInfo.InvariantCulture) too.Divot
If you want to include the timezone offset, do this: dt.ToString("s") + dt.ToString("zzz") // 2013-12-05T07:19:04-08:00Macur
Grrrr. Time is such a complex topic. Tnx mr Fleming!Solitaire
The slashes (\:) cause issues with the string... put in an @ character to use a string literal instead.Mawkin
Interestingly, the link for "DateTime Formatting Options" doesn't include "o" in its list of string formatters. Strange.Christly
@core: that's one of the standard Formats, which is different from the custom Formats linked: msdn.microsoft.com/en-us/library/az4se3k1(v=vs.110).aspxHipparchus
Please put the simplest, least code, least likely to be messed up option of "o" first.Cockloft
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");.Zeph
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.Bobcat
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.Unwonted
+00:00 as an offset to represent UTC is valid according to ISO8601 specs (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.Adjudication
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: 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.Innominate
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 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.Gamba
also, if you put it into an interpolated string: $"...{Datetime.UtcNow:o}..." will do the trickExaggerated
K
506

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".

Kumkumagai answered 22/9, 2008 at 14:6 Comment(13)
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"Provisional
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.Dulse
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 (.)Dulse
@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.”Cockloft
@Cockloft Are you sure s is invariant? From your link (jump to format strings) it says s uses SortableDateTimePattern, 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.Jidda
@drzaus, DateTimeFormatInfo is a sealed class. In the enumeration of types of format strings, 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 ;-).Cockloft
@Cockloft - now I'm very confused. According to the documentation I linked earlier for SortableDateTimePattern, 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)).Jidda
@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.Cockloft
"o" is superior.Cockloft
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, msdn.microsoft.com/en-us/library/….Botulinus
Whether you choose "o" or "s" 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.Elemi
"o" doesn't give me the Z, in .net 5. It just gives me 7 places after the decimal point in seconds. Car crash.Feasible
How to trim those damn milliseconds using "o", especially when it's all 0's?Fluorescein
S
118
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")
Stander answered 22/9, 2008 at 13:59 Comment(13)
Just out of interest: Why string.Concat() rather than '+'?Aubade
Habbit, is there a difference?Stander
@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.Zane
Yes, Mark is correct. Koen, you have just fallen into the trap of an absurdly premature micro-optimisation, even if you are correct.Flub
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 optionsJidda
@greg84: Well, you are not entirely right. Look at this post by Microsoft architect Rico Mariani: 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.Dalenedalenna
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.Koopman
Actually string.Concat hurts you because the compiler optimizes + but not String.Concat. https://mcmap.net/q/46258/-does-c-optimize-the-concatenation-of-string-literalsWareroom
@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.Collop
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.Cockloft
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:DFervent
You know the answer is old when the example contains a date that's over 10 years ago. Hey it still works flawlessly!Allanite
@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[]).Wareroom
H
53

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:

Hinson answered 17/9, 2012 at 22:10 Comment(1)
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:00Papeete
D
53

Use the o standard format specifier to produce an ISO 8601 compliant date string.
If it has to end with Z instead of the timezone offset (-00:00, +05:00, -07:00) then use a DateTime having Kind = DateTimeKind.Utc:

System.DateTime.UtcNow.ToString("o")
// 2013-10-13T13:03:50.2950037Z

To control the number of digits in fractional seconds part, edit and use the expanded form of the standard format specifier which is:

"yyyy-MM-ddTHH:mm:ss.fffffffK"
// the T above is a literal, not a format specifier
Dulse answered 13/10, 2013 at 13:5 Comment(6)
Agreed this is the only way to be absolutely sure that you have an unambiguous date/time across any timezoneNautch
I do this in .net 5 and get no Z.Feasible
any way to get rid of the ticks? they are too specific?Arms
Works great for me in .NET 7. Just make sure you're using a UTC DateTime (not a DateTimeOffset) to get the trailing Z. You might need to call .ToUniversalTime() if your DateTime isn't UTCClapper
@Feasible In learn.microsoft.com/en-us/dotnet/standard/base-types/… The time zone component of DateTimeKind.Local date and time values is an offset from UTC (for example, +01:00, -07:00). All DateTimeOffset values are also represented in this format. The time zone component of DateTimeKind.Utc date and time values uses "Z" (which stands for zero offset) to represent UTC. DateTimeKind.Unspecified date and time values have no time zone information. So you'll only get a Z when you pass a UTCAdiabatic
I noticed I only get a "Z" if there is no time component, eg: 2024-01-22T00:00:00.0000000 and 2024-01-22T10:16:31.5474794ZWyne
S
35

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://mcmap.net/q/48592/-given-a-datetime-object-how-do-i-get-an-iso-8601-date-in-string-format to compare how this performs.

tl:dr; it's at the expensive end but still just a little over 650 nanoseconds 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 *****

Siccative answered 16/5, 2019 at 16:49 Comment(3)
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.Encrimson
Also that doc claims "u" is ISO 8601, but what's with the space instead of T? get it together microsoftEncrimson
@Encrimson en.wikipedia.org/wiki/ISO_8601#cite_note-30 ISO 8601 is relatively permissive if you read through it...Siccative
C
29

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.

Canadianism answered 6/7, 2015 at 16:11 Comment(3)
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.Hollandia
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.Interjection
@BlairConnolly You were right. The "z" format specifier should have been lowercase. As indicated here, the capital "Z" is only valid when your date is actually in UTC.Empty
P
28

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)

Pastern answered 17/9, 2014 at 14:31 Comment(0)
M
16

I would just use XmlConvert:

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

It will automatically preserve the time zone.

Mirianmirielle answered 2/6, 2011 at 15:19 Comment(1)
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); } }Encyclopedic
F
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:

Forejudge answered 5/12, 2014 at 17:11 Comment(1)
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).Conscript
C
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

Carmichael answered 30/8, 2018 at 19:5 Comment(1)
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"Carmichael
H
10

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

Hondo answered 4/5, 2017 at 22:21 Comment(0)
T
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)
Tying answered 26/11, 2008 at 21:55 Comment(0)
A
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

Ashford answered 12/9, 2012 at 10:12 Comment(2)
So it is okay to use .ToString("s")?Pinite
I believe so. - As long as your requirement matches the original question that is.. But do take a look at the warning by simon wilson belowAshford
D
4

Using Newtonsoft.Json, you can do

JsonConvert.SerializeObject(DateTime.UtcNow)

Example: https://dotnetfiddle.net/O2xFSl

Devilfish answered 20/5, 2019 at 7:23 Comment(2)
Installing a external dependency for a simple job like that is just overkillFreeman
@JérémieLeclercq Yeah, it would be. But a lot (maybe most?) of C# projects already have it in their dependencies, especially when in need for this specific date format. That's why I chose this method, and added this answer to the already correct ones.Devilfish
B
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)
Building answered 27/3, 2013 at 10:17 Comment(3)
SharePoint, when your .Net isn't Java enough.Dulse
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.Proverbial
Even in SharePoint hopefully you can use the BCL’s .ToString("o") or, better, $"My complicated string {dt:o}".Cockloft
B
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);
}
Bocage answered 22/6, 2018 at 13:22 Comment(1)
I think this ISO date string is culture invariant per definition.Warmedover
G
1

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);
    });
Grolier answered 6/9, 2019 at 16:33 Comment(0)
S
-1
DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")

Here you have your answer in its 'glorious' simplicity 😂!

Sungkiang answered 31/3, 2023 at 12:0 Comment(1)
This is already part of the highest voted Answer.Brod

© 2022 - 2025 — McMap. All rights reserved.