1

I've been having some issues which seem really really strange to me and I cannot event begin to comprehend why this is happening.

Basically, I'm trying to do a DateTime.ParseExact which if working on one case but not on the other.

Let's say I have this string as the date string:

"11/02/2015 11:59:06:313"

If I parse by giving the method the explicit declaration of the string, i.e. the next code, everything works fine:

DateTime.ParseExact("11/02/2015 11:59:06:313", "dd/MM/yyyy HH:mm:ss:fff", null);

Now, when placing this as a dynamic value (which is what I want) I get the "String not recognized as a valid DateTime format", in this code:

DateTime.ParseExact(item.original, "dd/MM/yyyy HH:mm:ss:fff", null);

I've also tried the Convert.ToDateTime method (which threw the same exception):

Convert.ToDateTime(item.original).ToString("dd/MM/yyyy HH:mm:ss:fff");

The 'item.original' variable is coming from a Class (it's a string property of that class, which is defined as

public class XmlData
{
    public string tableName { get; set; }
    public string columnName { get; set; }
    public string original { get; set; }
    public int required { get; set; }
    public string status { get; set; }
    public string type { get; set; }
    public string newValue { get; set; }
}

I'm really lost here. Does it make sense to anyone why this is happening?

EDIT

Decided to give a little more information on how this is being used because maybe the problem comes from further behind.

I have a class that I'm using to only define properties that, using reflection will create a DataTable.

The class has this property:

public DateTime last_date { get; set; }

Then I'm using this method to build the DataTable:

public DataTable CreateEmptyDataTable(Type myType)
{
    DataTable dt = new DataTable();

    foreach (PropertyInfo info in myType.GetProperties())
    {
        dt.Columns.Add(new DataColumn(info.Name, info.PropertyType));
    }

    return dt;
}

After initializing the DataTable, I'm reading the values from my XmlData class and using the following code to assing the value to the last_date column like this:

//Set the original state
foreach (XmlData item in collection)
{
    if (item.tableName == "vehicle")
    {
        if (item.original == "--NULL--")
            dr[item.columnName.Substring(item.tableName.Length + 1)] = DBNull.Value;
        else
        {
            if (item.type == "DT") //DateTime in format "dd/MM/yyyy HH:mm:ss:fff"
                dr[item.columnName.Substring(item.tableName.Length + 1)] = DateTime.ParseExact(item.original, "dd/MM/yyyy HH:mm:ss:FFF", null);
            else
                dr[item.columnName.Substring(item.tableName.Length + 1)] = Convert.ChangeType(item.original, dt.Columns[item.columnName.Substring(item.tableName.Length + 1)].DataType);
        }
    }
}
Zed_Blade
  • 1,009
  • 3
  • 18
  • 38
  • 3
    Have you replaced `null` with `CultureInfo.InvariantCulture`? – Farhad Jabiyev Mar 09 '15 at 11:44
  • The string you passed isn't in the ISO format, so it was interpreted as a loca datetime string. I suspect your computer doesn't use this particular long datetime format – Panagiotis Kanavos Mar 09 '15 at 11:45
  • The last approach does something different, it doesn't use the format to parse the string but to convert the (hopefully parsed) `DateTime` back to string. – Tim Schmelter Mar 09 '15 at 11:51
  • 1
    Use a debugger and find out what "item.Original" consists of. Also, make sure you dump out the length of that string, it might have some hidden characters. – Erti-Chris Eelmaa Mar 09 '15 at 11:54
  • @TimSchmelter Correct. I was trying to force the date format on a string because of possible Culture issues. But even that doesn't seem to be working – Zed_Blade Mar 09 '15 at 11:55
  • Can you change the string's format to the ISO format? Otherwise you will always have to know the exact culture used for your date – Panagiotis Kanavos Mar 09 '15 at 11:56
  • @ChrisEelmaa good point. I'll see if the length differs. But I know that the item.original lenght is 23 – Zed_Blade Mar 09 '15 at 11:56
  • The plot thickens - string converted data that comes from a database. Is the date stored as a string or a `datetime` in the database? If it's datetime, consider changing the `XmlData` type to a generic one that can return the actual type or at least change the payload to `object`, to avoid conversions. String conversions cause a lot database problems and should be avoided whenever possible – Panagiotis Kanavos Mar 09 '15 at 11:59
  • @ChrisEelmaa did the test. variable length and "hardcoded" lenght are the same: 23 – Zed_Blade Mar 09 '15 at 12:02
  • If you have to use a string, format datetimes using the ISO 8601 format (`ToString("o")`). It's also the standard datetime format for XML files. See [here](http://stackoverflow.com/questions/254753/what-is-the-correct-format-to-use-for-date-time-in-an-xml-file) for a similar question – Panagiotis Kanavos Mar 09 '15 at 12:02
  • The length won't help, it's the same for all these formats (including the ISO 8601 format). Do you *have* to use this specific format? Is the original datetime in this format or was it accidentally generated by some code between the database and your code? – Panagiotis Kanavos Mar 09 '15 at 12:04
  • @PanagiotisKanavos on the DataBase it's defined as a DateTime – Zed_Blade Mar 09 '15 at 12:06
  • Then the original problem is in the code that generates XmlData - it generates localized strings that can fail in various cases. Eg this will also fail in countries that use `,` as a decimal separator (eg Germany). You need to use InvariantCulture both when generating the strings and when parsing them, to avoid such issues. – Panagiotis Kanavos Mar 09 '15 at 12:13
  • @PanagiotisKanavos I'm not understanding what you mean by using the InvariantCulture when generating the string. Shouldn't a string just be a string? I'm reading this value exactly as it comes from the Xml File, i.e., reading a string and placing it on a string variable – Zed_Blade Mar 09 '15 at 12:16
  • 1
    The problem is in the code that generates the XML file. It creates localized strings. Unless *that* code is changed to work using InvariantCulture and/or export dates as ISO8601, you'll need to know the culture and format it used, so you can use them as well during parsing. ISO8601 is the standard format for datetimes in XML, changing it only leads to confusion as you found out – Panagiotis Kanavos Mar 09 '15 at 12:40
  • @PanagiotisKanavos I don't exclude that option but the date in that format is an absolute requirement for acceptance. This particular column is then used to perform validation on update on the database (a timestamp for the last change. if what I'm sending differs from the value on the DB, then the update must fail) – Zed_Blade Mar 09 '15 at 13:35
  • 1
    @Zed_Blade but it *already does differ*. The database doesn't store any formatted strings, datetimes are binary. It's the custom XML-generating code that uses the strange format. In SQL Server the ISO format is also unambiguous so conversions from ISO strings to datetime won't cause issues. The real fix is to ensure all strings use ISO. The band-aid is for your code to use InvariantCulture instead of `null` - but expect you or someone else to stumble two months down the line, when you forget about the fix – Panagiotis Kanavos Mar 09 '15 at 13:44
  • @PanagiotisKanavos I get your point. I think you're right. No matter what I do now, it won't recognize that as a valid DateTime string.. – Zed_Blade Mar 11 '15 at 15:12

3 Answers3

4

Your format is correct.

Since you use null as an IFormatProvider, I strongly suspect your CurrentCulture has different DateSeparator than / or/and different TimeSeparator than : characters.

/ and : characters are special in custom date and time parsing. They means as: replace me with current culture date or time separator

In your profile, it says you are from Portuguese, and your current culture will be probably pt-PT. And this culture has - as a DateSeparator

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
  • The computer I'm working on has fr-BE (i think) culture. If it's not that, it's fr-FR. What doesn't make sense to me is that, with the same exact code (IFormatProvider null) if the string is hardcoded everything goes fine, but once I use that string coming from a class property I get the error. See my edit. – Zed_Blade Mar 09 '15 at 11:52
  • So the answer is: either use `DateTime.ParseExact("11/02/2015 11:59:06:313", "dd/MM/yyyy HH:mm:ss:fff", CultureInfo.InvariantCulture);` or `DateTime.ParseExact("11/02/2015 11:59:06:313", "dd'/'MM'/'yyyy HH:mm:ss:fff",null)`(apostrophes around). – Tim Schmelter Mar 09 '15 at 11:53
  • In Belgium `.` is used [in certain cases](http://en.wikipedia.org/wiki/Date_and_time_notation_in_Belgium) – Panagiotis Kanavos Mar 09 '15 at 11:55
  • @Zed_Blade `fr-BE` culture is fine for your parsing operation. It has already `/` and `:` as separators. Have you ever debug your code and see `item.original` is exactly `11/02/2015 11:59:06:313`? If so, `DateTime.ParseExact(item.original, "dd/MM/yyyy HH:mm:ss:fff", null);` should work either. – Soner Gönül Mar 09 '15 at 11:57
  • @SonerGönül see my reply to ChrisElmaa on the original post – Zed_Blade Mar 09 '15 at 12:04
3

Just a shot in the dark:

You have a working hardcoded value

DateTime.ParseExact("11/02/2015 11:59:06:313", "dd/MM/yyyy HH:mm:ss:fff", null);

But when you assign the value to the last_date column:

if (item.type == "DT") //DateTime in format "dd/MM/yyyy HH:mm:ss:fff"
            dr[item.columnName.Substring(item.tableName.Length + 1)] = DateTime.ParseExact(item.original, "dd/MM/yyyy HH:mm:ss:FFF", null);
        else
            dr[item.columnName.Substring(item.tableName.Length + 1)] = Convert.ChangeType(item.original, dt.Columns[item.columnName.Substring(item.tableName.Length + 1)].DataType);

shouldn't that be

DateTime.ParseExact(item.original, "dd/MM/yyyy HH:mm:ss:fff", null);

instead of

DateTime.ParseExact(item.original, "dd/MM/yyyy HH:mm:ss:FFF", null);

?

MReis
  • 56
  • 1
  • 2
0

In case anyone is wondering, the "solution" for this problem was to instantiate a DateTime object by deconstructing the string and assigning it on the DateTime constructor like this:

string[] date = item.original.Split(' ');
string[] datePart = date[0].Split('/');
string[] hourPart = date[1].Split(':');

DateTime newDateValue = DateTime.MinValue;

if (hourPart.Length == 3)
{
    newDateValue = new DateTime(Convert.ToInt32(datePart[2]), Convert.ToInt32(datePart[1]), Convert.ToInt32(datePart[0]), Convert.ToInt32(hourPart[0]), Convert.ToInt32(hourPart[1]), Convert.ToInt32(hourPart[2]));
}
if (hourPart.Length == 4)
{
    newDateValue = new DateTime(Convert.ToInt32(datePart[2]), Convert.ToInt32(datePart[1]), Convert.ToInt32(datePart[0]), Convert.ToInt32(hourPart[0]), Convert.ToInt32(hourPart[1]), Convert.ToInt32(hourPart[2]), Convert.ToInt32(hourPart[3]));
}

I say "solution" with quotes because I think that's a bad solution and there's probably a better way around it because this operation is relatively heavy but at least it works

Zed_Blade
  • 1,009
  • 3
  • 18
  • 38