Andreas
Andreas

Reputation: 2075

How to create .NET CultureInfo with Dateformat ISO 8601?

Is it possible to make .NET create the following output?

DateTime.UtcNow.ToString() --> "2017-11-07T00:40:00.123456Z"

Of course there is always the possibility to use ToString("s") or ToString("yyyy-MM-ddTHH:mm:ss.fffffffK"). But is there a way to adjust the default-behaviour for the parameterless ToString-Method to the desired output?

I tried changing the CurrentCulture. But the best I got was "2017-11-07 00:40:00.123456Z". I did not find a way to change the separator between the date and the time from a space to "T".

Upvotes: 8

Views: 4203

Answers (2)

Christian Gollhardt
Christian Gollhardt

Reputation: 17004

Scott Hanselmann has blogged about it here.

a little Reflectoring shows us that the default format string for System.DateTime is "G" as in System.DateTime.ToString("G") where G is one of the presets.

[...]

And gets the output he expects, indicating that "G" is the combination of a ShortDate and a LongTime.

So you should override ShortDatePattern and LongTimePattern:

I converted the code to C# and yes, it is working:

var customCulture = new CultureInfo("en-US")
{
    DateTimeFormat =
    {
        ShortDatePattern = "yyyy-MM-dd",
        LongTimePattern = "HH:mm:ss.FFFFFFFK"
    }
};

Console.WriteLine(DateTime.Now);

System.Threading.Thread.CurrentThread.CurrentCulture = customCulture;
System.Threading.Thread.CurrentThread.CurrentUICulture = customCulture;
Console.WriteLine(DateTime.Now);

Console.ReadLine();

However, Scott has titled his post Enabling Evil for reason. Think twice before doing that!

The T is not needed, but also can not be provided. If you still need it, you need to use Reflection, as Matt answered.

Upvotes: 4

Matt Johnson-Pint
Matt Johnson-Pint

Reputation: 241525

It is possible, but only by accessing an internal field via reflection, which is not guaranteed to work in all cases.

var culture = (CultureInfo) CultureInfo.InvariantCulture.Clone();
var field = typeof(DateTimeFormatInfo).GetField("generalLongTimePattern",
                                           BindingFlags.NonPublic | BindingFlags.Instance);
if (field != null)
{
    // we found the internal field, set it
    field.SetValue(culture.DateTimeFormat, "yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK");
}
else
{
    // fallback to setting the separate date and time patterns
    culture.DateTimeFormat.ShortDatePattern = "yyyy-MM-dd";
    culture.DateTimeFormat.LongTimePattern = "HH:mm:ss.FFFFFFFK";
}
CultureInfo.CurrentCulture = culture;

Console.WriteLine(DateTime.UtcNow);  // "2017-11-07T00:53:36.6922843Z"

Note that the ISO 8601 spec does allow a space to be used instead of a T. It's just preferable to use the T.

Upvotes: 7

Related Questions