Date field value is changing after saving data to database [duplicate] - c#

I have a legacy application where the input is a date string, i.e.:
06/12/2009
The format of the input is always a string, and is consistent, it's always dd/mm/yyyy
At the moment the legacy app just INSERTS this in a DateTime fields. Obviously if the Localization Culture settings of the Server change, we have a bug.
Two questions:
One:
Whats the safest way to store Dates in SQLServer in this situation?
Is there a format that will always be correctly interpreted regardless of the order of day and month?
Two:
What settings exactly determines the culture of a SQLServer DB, is it an OS setting, or a setting of that DB, or what?
cheers

Format YYYY-MM-DD is unambiguous, meaning that SQL Server won't confuse the month
and day when converting a string value to DATETIME. (I've never experienced a problem with an implicit conversion using that format using the four digit year.)
The "safest" (and most convenient) way to store date values in SQL Server is to use DATETIME datatype.
Use the CONVERT function to explicitly specify the input and output formats when converting between DATETIME and strings.
SQL Server 2005 Documentation on CONVERT style argument values:
http://msdn.microsoft.com/en-us/library/ms187928(SQL.90).aspx
To convert a string representation to DATETIME datatype:
select CONVERT(datetime, '2009-06-03', 20)
The first argument is datatype to convert to, the second argument is the expression to be converted, the third argument is the style.
(style 20 is ODBC Canonical format = 'YYYY-MM-DD HH:MI:SS' (24 hour clock)
[FOLLOWUP]
To convert a DATETIME expression (e.g. getdate() to VARCHAR in 'YYYY-MM-DD' format:
select CONVERT(varchar(10), getdate(), 20)
Note that specifying varchar(10) gets you just the first 10 characters of the etnire 'YYYY-MM-DD HH:MM:SS' format.
[/FOLLOWUP]
As to what determines the default formats, that's going to be research. We avoid the issues caused by default formats by specifying the formats.

I would recommend storing all dates in UTC time when they are placed into the database. It will be consistent that way.
Storing dates like this seems to work well...
YYYY-MM-DD

See SET DATEFORMAT. The SQL 'culture' is set by SET LANGUAGE at a session level. SQL Server has its own date format settings, independent of the hosting OS. This is for several reasons: ANSI compliance, to prevent OS changes from affecting applications using the database hosted on that host and not least is compatibility, the SQL long predates the OS is currently running on.

Keep in mind that DATA is not its PRESENTATION. In this case that DATA is a DATE or DATETIME, regardless of how you show them.
As for inserting/updating/comparing datetime values, I quote the BOL:
When specifying dates in comparisons
or for input to INSERT or UPDATE
statements, use constants that are
interpreted the same for all language
settings: ADO, OLE DB, and ODBC
applications should use the ODBC
timestamp, date, and time escape
clauses of:
{ ts 'yyyy-mm-dd
hh:mm:ss[.fff] '} such as: { ts
'1998-09-24 10:02:20' }
{ d 'yyyy-mm-dd'} such as: { d '1998-09-24' }
{ t 'hh:mm:ss'} such as: { t '10:02:20'}
I can assure you that, if you use this formats they will always work, regardless of the locale of you server

I'm a bit conservative in these matters, but I prefer to use separate Year / Month / Day fields in the table, rather than a Date field that uses a DBMS-specific data type. It certainly takes more space, but the lack of ambiguity and increased portability is worth it to me.
The price you pay is that you don't get free date/time arithmetic and sorting, but it's easy enough to do yourself or by a slightly more complex "ORDER BY" clause.

I agree with the advice from spencer7593, but please be aware that using cast or convert without a format can give unexpected results. This T-SQL query returns 12, not 1.
set language British
select month(CAST('2016-01-12' AS datetime))

Normally I prefer to insert as
insert into tbl values('yyyyMMdd')
Then, itll be inserted in proper format based on db.

Related

Is this c# code enough to handle globalization?

My C# winforms program will be used in following countries
United Kingdom : date format day-month-year , currency separator is '.'
United States : date format month-day-year , currency separator is '.'
Denmark : date format day-month-year , currency separator is ','
I want to make the program run properly irrespective of what regional settings are on users computer. My main concern is handling date format and currency fields
(Language translation is not a problem because the program will only show English text)
To do this I have decided that all dates in the database will be saved with yyyy-mm-dd format and all decimal fields will be saved with . as separator.
I created the database with Danish_Norwegian_CI_AS collation .
So I am assuming the data will be saved in the above datetime format & decimal format without me requiring to do anything special.
I have put the following code in my program
var cult = new CultureInfo("en-GB");
Thread.CurrentThread.CurrentCulture = cult;
Thread.CurrentThread.CurrentUICulture = cult;
CultureInfo.DefaultThreadCurrentCulture = cult;
CultureInfo.DefaultThreadCurrentUICulture = cult;
Please help me by telling from your experience if the above code is enough to make my program safe
Culture applies only in the following two scenarios:
Converting a string representation of a value to a native format, which is called "parsing".
Converting the native format of a value to a string representation, which is called "formatting".
When you store a datetime or a decimal or some other type into the database, it is stored in a native format. In a SQL database, this is usually some compact binary value that you will never work with directly.
Consider the following SQL:
declare #dt datetime
set #dt = '01/02/2015 12:34:56'
select #dt
In the first line, we declare a variable of type datetime. It's not a string, it's a specific data type that takes up 8 bytes of memory or disk.
In the second line, we assign a string value to the variable. SQL parses the string, converting it to a datetime so it can be stored in the #dt variable. The actual value stored has a hexadecimal representation of 0x0000A41400CF5940.
When it did the parsing, the current culture for the environment where the code was running was applied. Because I am in the USA, it interpreted the date as January 2nd. If I was in Europe, it would interpret the date as February 1st (changing the internal value to 0x0000A43200CF5940).
Using dates in yyyy-mm-dd format will avoid misinterpretation, but that does not mean that the actual value is stored as a string in that format. It's just that the format is unambiguous, so it will be parsed the same way regardless of culture.
In the third line of code above, we select the variable to include it in a result set. Though we are selecting it in its native form without any conversion, we ultimately see it in a string representation. If you are running the query in a tool such as SQL Server Management Studio, the output window will format the native values to strings so you can read them. When doing so, the current culture is again applied. SQL's default is to show dates in yyyy-mm-dd format, rather than the culture-specific format. But other values, such as decimals, will use the current culture's separator.
If instead of running this in SSMS you actually retrieved the results through your own code in a SqlDataReader (for example), then formatting never occurs. The reader maps SQL's binary native value directly to the appropriate .NET native type, using the mappings shown here. A SQL datetime gets natively mapped to a .NET DateTime.
DateTime dt = (DateTime) reader["dt"];
Now quite often, you see someone doing silly things like this:
DateTime dt = Convert.ToDateTime(reader["dt"].ToString());
This is wasteful because the value is already a DateTime, and this code would use the current culture to format the string, then use it again to parse the string. That's a lot of string manipulation for no reason whatsoever.
Ultimately, in your .NET code, you will end up using that DateTime value and converting it to a string somewhere for output. When you do, that's when you apply the current culture.
Likewise, when you receive an input string from your user (such as when filling out a form), you parse the value to a DateTime using the current culture again.
Native data types are not strings - and are thus not affected by culture.
Notes:
If you want to see the hexadecimal representation of the native binary form of any SQL data type, you can use something like: select convert(varbinary, #dt)
Be aware of whatever the native format is for anywhere you are working. If you're writing to an http stream, a text file, or a document database, etc., the string representation does indeed matter, because a string is the native format in those scenarios.
And just to prove this applies to more than dates, consider:
select 123, 123.45, convert(varbinary, 123), convert(varbinary, 123.45)
--results: 123 123.45 0x0000007B 0x0502000139300000

Date type in MySql

I'm new to MySQL and C#.
I stored certain values in a column with data type Date. I did not want the time, only the date to be stored.
On viewing these values using phpMyAdmin or MySql command line, I see them in the format:
YYYY-MM-DD
However, when I retrieve these values in to my web application, they are displayed in the following format:
YYYY-MM-DD HH:MM (the time is specifically 12:00).
Why does this happen? And how can I prevent this from happening?
when you store in C# your date field, you use DateTime object. In this object when you don't specify the time part will be put a default value depends on Globalization.
You can study how DateTime works here
You can convert the date to the format you like when you fetch the data, using date_format():
select date_format(datecol, '%Y-%m-%d')
This returns the value as a string.
You shouldn't retrieve the value as a string from mysql. Why? Because if you ever need to do any operations on that value, such as adding a day, then you will need to parse it back into a DateTime again. String parsing can be slow, and when it comes to dates they are prone to errors like misinterpretation of mm/dd/yyyy and dd/mm/yyyy formatting.
The problem you have is that .NET does not have just a Date type. It only has a DateTime type. So loading a MySQL DATE type, is going to get a DateTime with the time portion set to midnight.
There's no direct problem with that, except on how are outputting the result. If you just call .ToString() without any parameters, or you implicitly use it as a string, then you are going to get a result with the full date and time. You simply need to provide a parameter to indicate what formatting you want.
Without any parameters, you are getting the General "G" format. This is explained in the documentation here.
In other words:
yourDateTime.ToString() == yourDateTime.ToString("G")
You can read about all of the other formats available, here and here.
In particular, if you just want the date, then you probably want to do this:
yourDateTime.ToString("d")
Based on your comments, you should be doing this instead:
MySQL Query:
SELECT Gbstartdate FROM TblGbDef
C#:
DateTime gb_start_date = (DateTime) datareader[0];

time format on my sql server

my c# program selects a datetime field from database and returns the data like the following:
21/06/2012 4:11:48 p.m.
it is not stored this way though (it's stored like 2012/06/21 15:19:10:000).
my locale is set to English/New Zealand
but when I try to save it back to database
the ISDATE() sql function doesn't like that format and just returns NULL. Do I have any options to set the time format in sql or c#? What in sql or c# is making it put "p.m."? (instead of just "PM", which by the way works)
Summing up the problem
See latest related link
You shouldn't be saving it in the database as text at all - you should be using the relevant data type such as DATETIME, and passing DateTime values from .NET to the database using SQL parameters. If you're using the right data type, the value won't actually be stored as "2012/06/21 15:19:10:000" at all. That may be how you see it in SQL Server Studio or something similar, but that doesn't mean it's the raw storage format.
Logically, a date/time doesn't have a format any more than the number ten is "10" (decimal) vs "A" (hex). Avoid string conversions as far as you can, and your life will be easier.
Note: See Jon's response about properly saving the DateTime in the database rather than a string.
For C# String formatting:
The DateTime object can output in different formats using the .ToString() function.
Please see the following links
Custom Date and Time Format Strings
DateTime.ToString Method
So, for example:
DateTime.Now.ToString("dd/MM/yyyy h:mm:ss");
That will output in the format you have in your question.
I think your issue is related to the Set language setting in sql.
Similar to this question: SQL Server ISDATE() Function - Can someone explain this?
Can you try to set your sql language to match your format?
Most of the SQL datetime formats should be recognizable by C# DateTime.Parse(youSQLDatetimeVariable). I find it a bit odd what's happening in your case.
Like other said you really shouldn't but here is how you can convert DateTime to various formats:
SELECT Convert(VARCHAR, MyDateTimeField, 112) from MyTable
That number (112 ) indicates the DateTime format that will appear as Varchar
All formats can be found here in details:
http://anubhavg.wordpress.com/2009/06/11/how-to-format-datetime-date-in-sql-server-2005/
Or within your C# code with
YourDatetimeVariable.ToString("MM-dd-yyyy")
Or whatever format you want within those quotes where MM- month number, dd - day number, yyyy - year.

SqlDatareader GetValue returning date value acording to system date format

Hi i am reading a value from a table using SqlDatareader, problem is it is always formatting the value acording to the machine date format settings.
for example the original date format in the source table is saved as
yyyy/mm/dd
when i use SqlDatareader.GetValue in a machine that has date set as MM/dd/YY
it is atutomatically converted to that format
is there anyway to retrive the date value with the original date formatting intact?
thanks
There is no "original date formatting", dates are kept internally as numbers, not strings.
Within a SQL query, you can choose the output formatting with
CONVERT(varchar(30), theColumn, nnn)
where "nnn" is one of the common date formats listed here: MSDN.
Personally I find that page confusing, so another (more useful) page is here: SQL Server Helper. From that page:
CONVERT(VARCHAR(20), GETDATE(), 100)
will return 'Jan 1 2005 1:29PM'
Probably not what you're looking for. But if you want to store the formatted date time, may be you should rather use VARCHAR or CHAR(N) as the field type. I would think the DATETIME field is to store datetime and the format it self isn't that important (or what it is meant for). However, you could reconvert it back to that format in C#.

String to DateTime in C# to save in SQL server

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
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.
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.
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); // <============
}
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))

Categories