tags:

views:

22

answers:

2

I'm attempting to integrate with the TeamCity REST API. It contains timestamps of the form 20100804T104413+0100. It's almost ISO 8601, but doesn't contain the hyphens.

How do I convert this from a string to a DateTime in .NET? I've tried:

DateTime.ParseExact(s, "yyyyMMddTHHmmss+zzzz", CultureInfo.InvariantCulture);

(and without the +zzzz), but I get String was not recognized as a valid DateTime.

+1  A: 

This works for me:

using System;
using System.Globalization;

class Test
{
    static void Main()
    {
        string text = "20100804T104413+0100";

        DateTime dt = DateTime.ParseExact
            (text,
             "yyyyMMdd'T'HHmmsszzz",
             CultureInfo.InvariantCulture);
        Console.WriteLine(dt);           
    }
}

You don't actually need the quotes around the "T", but I tend to put them in to make the pattern clearer. Likewise the "zzz" can be "zzzz" - but I think the problem was the "+" you had before the "zzz".

Note that if you reformat it using the same format, you'll end up with a ":" in the middle of the offset specifier :(

Jon Skeet
I'd like to suggest using DatetimeOffset instead, otherwise you'll loose your TZ info in both ways, risking introducing offsets that weren't there before.
Abel
+1  A: 

I know the q. is already answered, but I believe a few notes are in place, if I may:

Problems with using DateTime

I would advice against using DateTime in your scenario, as it seems likely that you want the representation to be equal when roundtripped (i.e., when converted back again to a string, the representation must be equal). The problem with DateTime is that it doesn't keep the exact time zone information.

With the proposed solution, the timezone information may change. This depends on input, but using the invariant culture, you will receive a DateTime object of {4-8-2010 11:44:13}. I thought this could be remedied by using DateTimeStyles.RoundtripKind as in the following example, but, considering that there is no timezone information in DateTime, this will still not work:

// DON'T DO THIS, dangerous code ahead:

DateTime time = DateTime.ParseExact(
     "20100804T104413+0100", 
     "yyyyMMdd'T'HHmmssK", 
     CultureInfo.InvariantCulture, 
     DateTimeStyles.RoundtripKind);

// loosing data when converting back:
string convertedBack = time.ToString("yyyyMMdd'T'HHmmssK");
// convertedBack now contains "20100804T114413+02:00"

Use DateTimeOffset instead

This problem goes away when you use DateTimeOffset for your tasks instead. The idea remains the same, but all of a sudden, roundtripping works:

// DO THIS instead:

DateTimeOffset parsedDate = DateTimeOffset.ParseExact(
     "20100804T104413+0100", 
     "yyyyMMdd'T'HHmmsszzz", 
     CultureInfo.InvariantCulture, 
     DateTimeStyles.RoundtripKind);

// converting back is now easy:
convertedBack = parsedDate.ToString("yyyyMMdd'T'HHmmssK");
// convertedBack contains the string "20100804T104413+01:00"

Of course, it depends on your situation whether the timezone information is important. But it wouldn't be the first time that 10 o'clock changed to 11 o'clock and finding the bug then can be hard.

Dealing with the colon in the timezone

As Jon Skeet mentioned, there's colon ":" in your output that wasn't there in the input. So, full roundtripping seems impossible. Unfortunately, while DateTimeFormatInfo in any culture can modified to change just about every appearance, i.e., you can set TimeSeparator to an empty string and the colons disappear in formatted times, this level of granularity is not available in the timezone offset. Even worse, Reflector shows that it is deliberately hard-coded (I once had the same issue with removing the "+" for positive offsets):

if (offset >= TimeSpan.Zero)
{
    result.Append('+');
}
else
{
    result.Append('-');
    offset = offset.Negate();
}
result.AppendFormat(CultureInfo.InvariantCulture, "{0:00}:{1:00}", new object[] { offset.Hours, offset.Minutes });

The solution is to either provide your own IFormatProvider, or to be pragmatic and just remove that colon:

convertedBack = parsedDate.ToString("yyyyMMdd'T'HHmmssK").Replace(":", "");
Abel
In this case, I'm not bothered about roundtrip fidelity, because it's for a read-only team dashboard; but it's all good information. Thanks.
Roger Lipscombe