Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parsing a TeamCity (Java?)-generated timestamp in .NET?

Tags:

.net

datetime

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.

like image 438
Roger Lipscombe Avatar asked Aug 04 '10 12:08

Roger Lipscombe


3 Answers

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 :(

like image 174
Jon Skeet Avatar answered Nov 15 '22 15:11

Jon Skeet


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(":", "");
like image 24
Abel Avatar answered Nov 15 '22 16:11

Abel


This is a little off topic in this old post but since I found the post when googling for parsing of Teamcity api dates, here's a Javascript version for the benefit of anyone else interested in the same thing:

moment("20150107T085108+0100", 'YYYYMMDDTHHmmssZ').format('YYYY-MM-DD HH:mm:ss')

It's using the excellent Moment.js library for the parsing.

like image 38
emilast Avatar answered Nov 15 '22 15:11

emilast