views:

343

answers:

4

Hello All:

I have an issue while converting "March 16-17" to DateTime and saving it to SQL server. "March 16-17" as it looks, read as March 16 to March 17, which in my case is invalid, but C# DateTime.TryParse() is treating "March 16 -17" as March 16, 2017 which is wrong, and saving the data in SQL server. SQL server treats "March 16-17" as invalid. So, can some body tell me how to use SQL server datetime validation in C#.

Thanks Ashwani

+4  A: 

Parsing

You can use DateTime.TryParseExact to parse a string to a DateTime, requiring it to be in an exact format. This will eliminate the problem, that you are able to parse an invalid date to a DateTime instance.

Here is an example:

        DateTime dt;
        if (DateTime.TryParseExact("March 16", "MMMM dd", new CultureInfo("en-GB"), DateTimeStyles.None, out dt))
            Console.WriteLine(dt);

When omitting the year, TryParseExact will assume the current year. If you pass in "March 16-17" to that method, it will fail. The IFormatProvider parameter is the english culture, so we can parse "March" to be the 3rd month of the year.

As you note, this is not the same as what SQL Server does. How it converts dates will be based on it's collation settings. I would not recommend it, but if you really, really need to replicate that functionality exactly, and use it from C#, you could create a Stored Procedure that takes a varchar, makes the conversion, and returns DateTime - and call it from C#.

You can also use DateTime.TryParse method to parse the date from C#. This method also takes an IFormatProvider, which tells the framework how to do the parsing. IFormatProvider is implemented by CultureInfo, so if you pass in the CultureInfo that corresponds to the SQL Server collation where you observed the desired behavior, chances are that the parsing results will be similar. Finally, you could do your own implementation of IFormatProvider if you are unsatisfied with the built-in possibilities.

My Comments

Your real problem is that you are in effect doing the validation and transformation from string to DateTime twice. Once in C#, then you send the string to SQL Server, and have that transform it again. This is bound to give you problems, since, as you noted, the two systems parse similarly, but not exactly the same way in edge cases.

What I think you should do, is to do your validation and parsing in C#, and then send the resulting DateTime to SQL Server as a DateTime object, so SQL Server itself needs to do no parsing. You can do this using parameterized queries in ADO .NET - if you use something else for data access, there will be a similar feature.

driis
DateTime.TryParseExact will need a format, but I donot want to put any format restrictions. SQL server detects "March 16-17" as invalid date format, can't we use this in C#?
Ashwani K
That is self-contradictory. You dont want to restrict the format - so all formats should be valid. Yet when a DateTime has been parsed, you decide that it is invalid. TryParseExact has a method that takes an array of formats, so you can specify multiple.
driis
No, what I am saying is that SQL server is not allowing this string (march 16-17) to save in date column but C# is converting it to valid date. Can't we use SQL server function in C#?
Ashwani K
I am doing the same thing which u suggested, And this is causing the issue. See for C# March 16-17 is valid and for SQL it is invalid and according to my requirement it should be invalid only.
Ashwani K
+4  A: 

It sounds as if the value shouldn't be a DateTime at all. Instead it should be a string (varchar/nvarchar) or two DateTime values (start and end). I.e., why are you trying to call DateTime.TryParse or DateTime.TryParseExact in the first place? If you want to store them as dates, you'll need to force the users to enter them as dates (as in two date values in your example) and then you can easily store them as dates. If you want to allow users to enter "March 16-17" or "Spring 2010" or "Last half of March", then use a varchar or nvarchar data type.

EDIT Given what you have said in comments, it sounds like you are passing the XML directly to SQL Server and hoping to have SQL Server parse the dates. Unfortunately, SQL Server is not great a parsing as you have discovered. IMO, it would be easier to rebuild the XML in C#, validating and parsing dates and integers, before you pass it to SQL Server. I.e., I would try to do as little of this type parsing and validation in SQL Server as possible. If you still want to go that route, another solution would be to create a CLR function (which means the CLR must be enabled) that would give you the same date parsing functionality as C#.

EDIT After much discussion, it sounds like the issue is that C#'s date parser is too clever for your purposes. What you want is for C# to invalidate the date as SQL Server would. I can think of a couple of ways to solve that:

  • Send a long list of allowed formats to the DateTime.TryParseExact method. The downside is that this is far less forgiving in terms of parsing date values.
  • Run TryParse and validate the year. If the year X number of years beyond this year, then invalidate it.
  • Find a way of forcing the source of the XML to enforce dates so that only valid dates are sent.
  • Write a routine that determines if the date has some wonkiness to it like 16-17 (although 03-16-17 should be considered valid, so you will need to be careful) before you pass it to TryParse.

Trying to actually use SQL Server's date parsing will not work unless you push the data into a character column and then use SQL Server's IsDate and Cast functions to populate the DateTime column after you have populated the data.

Thomas
I have an application, in which use can upload XMl files which contain some nodes of value type DateTime. So before entering data in SQL server, I am using DateTime.TryParse to check if the data in datetime field is correct or not, but it is allowing this string. But if I try to convert this string in SQL server and store in database it is giving taking it as invalid.
Ashwani K
Don't TryParse in C#, and then pass the value along to SQL Server as a string. Pass it to SQL Server as a DateTime using a parameterized query.
driis
But calling database to validate each date data will be a serious performance hit, if suppose XMl contains 10000 date data rows
Ashwani K
No, I am parsing date in c# and then passing the converted date to SQl server, that is why I am getting error
Ashwani K
Are you passing the date to SQL Server as a DateTime into a DateTime column meaning the value is an instance of a `DateTime` struct? As a curiosity, how would you want "March 16-17" to parse given that March 16, 2017 is incorrect?
Thomas
It should be discarded as invalid by C# itself
Ashwani K
@Ashwani K - Wait. I think I now understand. You want C# to invalidate the date rather than parse it. I.e., C# is being too clever for what you want. Is that correct?
Thomas
Yea, now u got it right :)
Ashwani K
A: 

It thinks 17 is 2017 because years can be writen in 2 digits try forcing it to be four

if (DateTime.TryParseExact("March 16-17", "MMMM dd YYYY", new CultureInfo("en-GB"), DateTimeStyles.None, out dt))
Gabriel Guimarães
+1  A: 

I understand that you don't want to restrict your users. You sound like you're doing something like this:

mySqlCommand.CommandText = "UPDATE tbl SET date_field = @parm";
string maybeDate = "March 16-17");
DateTime dt;
if (DateTime.TryParse(maybeDate, dt)
{
    mySqlCommand.Parameters.AddWithValue("@parm", maybeDate)
}

Can you do this instead?

mySqlCommand.CommandText = "UPDATE tbl SET date_field = @parm";
string maybeDate = "March 16-17");
DateTime dt;
if (DateTime.TryParse(maybeDate, dt)
{
    mySqlCommand.Parameters.AddWithValue("@parm", dt); // <============
}
egrunin