Date Parameters passed to stored procedure not working as expected - c#

I faced an issue in my project where i passed the date as 09/03/2013 23:59:59 to stored procedure but saw in profiler .net converted it to 09/04/2013 00:00:00.
To confirm I created a small test application(anybody can use it to replicate, I am using .Net 4.5 and Sql server 2012 express edition).
Below is test code:
DateTime startdate = DateTime.Parse("09/03/2013");
DateTime endDate = startdate.AddDays(1).AddTicks(-1);
try
{
using (SqlConnection konekcija = new SqlConnection(ConfigurationManager.ConnectionStrings["default"].ToString()))
{
konekcija.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = konekcija;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "[Interface].[uspTestDateParameter]";
cmd.Parameters.AddWithValue("#CurrentDate", startdate);
cmd.Parameters.AddWithValue("#BatchEndDate", endDate);
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
{
// Fill the DataSet using default values for DataTable names, etc
DataSet dataset = new DataSet();
da.Fill(dataset);
DataTable dt = dataset.Tables[0];
//return dataset;
}
}
}
}
catch (Exception ee)
{
}
Below is the procedure:
CREATE PROCEDURE [Interface].[uspTestDateParameter]
(
#CurrentDate DateTime
,#BatchEndDate DateTime
)
AS
BEGIN
Declare #table table (strt Datetime ,endT Datetime )
Insert into #table values (#CurrentDate,#BatchEndDate)
Select * from #table
END
The resultset returned is 9/3/2013 12:00:00 AM 9/4/2013 12:00:00 AM
I could have attached screen shot of Dataset visualizer but cannot do so as it requires reputaion 10. but above are the values of two columns(strt,enDt) I get.
Can somebody please help? My procs failing in production due to this.

DATETIME is rounded as described here:
http://technet.microsoft.com/en-us/library/ms187819.aspx
This article explicitly states that all values arounded to .000, .003 or .007 seconds. The user-specified DateTime 01/01/98 23:59:59.999 will always be stored as 1998-01-02 00:00:00.000.
Instead of using ticks, why can't you do the following?
DateTime endDate = startdate.AddDays(1).AddSeconds(-1);
This would actually pass the date you've said you're passing (09/03/2013 23:59:59) instead of one tick short of the next second.
Alternatively, use DATETIME2 as your SQL datatype, which, according to documentation, has an accuracy of 100ns (one tick):
http://technet.microsoft.com/en-us/library/bb677335.aspx

The issue is with rounding of the SQL Server datetime type, as AntP described.
There are two different solutions you could consider:
Option #1
Use a datetime2 type in SQL Server, as Tim suggested. It has a higher precision, so you wont be likely to round. This is still tricky though as you have to be aware of how much precision you are sending and how much the type will support. In other words, should it be 23:59:59.999 or should it be 23:59:59.999999 or will 23:59:59.0 suffice? You will have to decide what makes sense for your application.
If your data always contains whole dates, you can change your input value to:
DateTime endDate = startdate.AddDays(1).AddSeconds(-1);
And that won't get rounded up, even with a datetime type.
Option #2
Use half-open interval ranges of [start,end). When the end date is exclusive, your queries are much simpler and you don't have to worry about precision. When two intervals border each other, the end of one interval will be exactly the same as the start of the next. There is never any ambiguity, because the end date is exclusive.
Instead of sending the range as 09/03/2013 00:00:00 to 09/03/2013 23:59:59, you send it as 09/03/2013 00:00:00 to 09/04/2013 00:00:00 with the understanding that the exact end date is excluded.
In other words, a date is in a range if:
StartOfRange <= #TheDate < EndOfRange
Or put in other terms:
StartOfRange <= #TheDate AND EndOfRange > #TheDate
On the .NET side of things, you can still present your input terms as fully inclusive. Just add a as appropriate to the end value before passing it into SQL. For example, if you are asking for whole dates as input, then add one whole day to the end date.

Tick is a very small unit of time (MSDN). There are 10,000 ticks in a millisecond.
Try to subtract 1-2 seconds to see if it works.
Btw, SQL Server datetime can only store 333 milliseconds (e.g.: .000, .003., .006 etc)

Related

Add days to DateTime returns value of 0?

This code counts number of records in an MSSQL table where a date is between today's date and today's date + 8 days, but it doesn't work; it returns a value of 0, but 2 is the right answer.
If I change DateTime.Now.AddDays to 7 or less it works as it should.
//Ordre klar til bestilling
command.CommandText = "SELECT COUNT(*) from bestillinger WHERE udlevering BETWEEN #date and #dateadd";
command.Parameters.AddWithValue("#date", DateTime.Now.ToString("dd/MM/yyyy"));
command.Parameters.AddWithValue("#dateadd", DateTime.Now.AddDays(+8).ToString("dd/MM/yyyy"));
con.Open();
command.ExecuteNonQuery();
string result0 = command.ExecuteScalar().ToString();
con.Close();
MessageBox.Show(result0);
if (result0 != "0")
{
bestillingToolStripMenuItem.ForeColor = System.Drawing.ColorTranslator.FromHtml("#FF1919");
}
Don't treat dates/times as strings. Just:
command.Parameters.AddWithValue("#date", DateTime.Now);
command.Parameters.AddWithValue("#dateadd", DateTime.Now.AddDays(8));
The problem could well be date/time formats.
Note that you are actually executing it twice for no reason; you can remove the command.ExecuteNonQuery().
Finally, don't treat integers as strings:
int count = (int)command.ExecuteScalar();
if(count != 0) { .... }
Seven days from now, it's still September, and the date looks like this:
30/09/2014
Eight days from now, it's October, and the date looks like this:
01/10/2014
Pop quiz: does 01/10/2014 refer to October 1st, or January 10th? Obviously, you mean for it to refer to October 1st, but parts of the world (and likely Sql Server) will instead read that as January 10th.
The solution here is that you shouldn't pass date parameters as strings! Just removing the .ToString() calls entirely is probably enough to fix it in this case, but I prefer to be explicit about my database types:
command.Parameters.Add("#date", SqlDbType.DateTime).Value = DateTime.Today;
command.Parameters.Add("#dateadd", SqlDbType.DateTime).Value = DateTime.Today.AddDays(8);
Of course, the above fix assumes that you've done the sane thing in your database. You may have made the bad mistake of storing your dates as varchar fields in the first place. If you did that, Sql Server is comparing your fields as strings, and based on your example format anything early in the month is always going to be sorted before dates later in the month, even across different months. This means that your example end date came before your start date. If this is your problem, you'll need to add a new Date column to your table and write code to update your rows with the converted date values. You'll probably need to coordinate this with fixing other parts of your application that insert, update, and select on this field.
The main lesson to take away from this: NEVER treat dates as strings! This applies to every level of your application except the presentation tier.

Inserting into DB showing Exception

I am inserting some values into db. In DB field received_date is type of datetime datatype I am using following code for inserting But it is showing some exception, I am unable to figure it out.
Exception:
The conversion of a varchar data type to a datetime data type
resulted in an out-of-range value.
SqlConnection con = new SqlConnection("data source=ATLBLRDP-19\\PRIME;database=arp;uid=sa;pwd=****;");
con.Open();
SqlCommand cmd_r = new SqlCommand();
cmd_r.Connection = con;
cmd_r.CommandType = CommandType.Text;
cmd_r.CommandText = "insert into raw_mails(received_date,sender,receiver,subject,body,has_parsed,has_attachments,created_by,created_on,mail_type) Values(#received_date,#sender,#receiver,#subject,#body,#has_parsed,#has_attachments,'" + DateTime.Now + "','" + DateTime.Now + "',#mail_type)";
cmd_r.Parameters.Add("#received_date", em.DateTimeReceived);
cmd_r.Parameters.Add("#sender", em.From);
cmd_r.Parameters.Add("#receiver", em.Receiver);
cmd_r.Parameters.Add("#subject", em.Subject);
cmd_r.Parameters.Add("#body", em.Body);
cmd_r.Parameters.Add("#has_parsed", 1);
cmd_r.Parameters.Add("#has_attachments", em.HasAttachments);
cmd_r.Parameters.Add("#mail_type", 4);
cmd_r.ExecuteNonQuery();
con.Close();
I checked with every query (parameters.add()) all are working but if I try to insert received_date then only it shows exception . Here em is type of EmailMessage.
And I am using sql server 2012 for DB purpose.
You don't give much to go on, but it is clear that you are setting a date value on your Sql server using a string.
There are definitely two and potentially three places you do this, depending on the type of em.DateTimeReceived. When you build your CommandText you also insert DateTime.Now twice, implicitly calling .ToString() for the conversion.
However, calling .ToString() will use your system's locale. For example, I am in the UK, so today's date (December 13) is written out as "13/12/2013 14:02:08". If I assign this string to a sql datetime it will fail, because my Sql server is using it's default US locale - so it reads 13 as the month and 12 as the day, and throws exactly the error you've seen.
So in order to fix this, you need to either:
output the dates as strings using an explicit format that matches your Sql server's collation (using DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss") or similar)
ensure that all of the dates passed in are actual DateTime variables and not strings, allowing the SqlCommand to ensure formatting is correct
The valid range for the datetime data type is 1753-01-01 through 9999-12-31 (Date and Time Data Types and Functions (technet))
If the em.DateTimeReceived property is smaller than 1753-01-01, you will get this error. Depending on data conversion from .NET to SQL, this might also be the case when DateTimeReceived is null.
Ensure that your property value is always greater than 1753-01-01 or use the datetime2 data type which has a range starting at 0001-01-01.
on a side note: Is there a specific reason you are still using the legacy datetime data type? Microsoft recommends "Use the time, date, datetime2 and datetimeoffset data types for new work. These types align with the SQL Standard. They are more portable. time, datetime2 and datetimeoffset provide more seconds precision. datetimeoffset provides time zone support for globally deployed applications." (msdn)
Either change the DateTime.Now to DateTime.Now.ToString("yyyy/MM/dd HH:mm") or pass them as parameters, similar to your other values.

Inserting a date into a database does not work (ASPX/C#)

I have this C# code:
string RegisterDate = DateTime.Now.ToString();
RegisterDate = RegisterDate.Remove(10);
RegisterDate = RegisterDate.Replace('/', '-');
RegisterDate = String.Join("-", RegisterDate.Split('-').Reverse());
Which gives thie result: 01-06-2013
The problem is that when I try to insert it to the table I get this result: 21/06/1894
When I get the date via input it works great in the same date format, so why in this case it doesn't work?
update
If I try this:
var RegisterDate = DateTime.Today.Date;
I get Error :
Syntax error (missing operator) in query expression
Wish for help, thanks!
Don't use a string conversion at all. Assuming your data type in the database is DateTime or something similar, just use a parameter and specify its value as the DateTime in your C# code to start with. (I'm assuming you're already using parameterized SQL rather than embedding data straight in your SQL. If you're not using parameters yet, start right away!)
I'd suggest using DateTime.Today to make it clearer that you're only interested in the date part. (Note that this means that the same code running in different places could end up inserting different dates - is that okay? Normally I don't like letting the system local time zone affect things.)
You should generally avoid string conversions unless you really need a string representation of the data. At other times they just cause trouble.
EDIT: You asked for an example. It would be something like:
using (var connection = new SqlConnection(...))
{
connection.Open();
using (var command = new SqlCommand(
"INSERT INTO Foo (Name, RegisterDate) VALUES (#Name, #RegisterDate)",
connection))
{
command.Parameters.Add(new SqlParameter("#Name", SqlDbType.NVarChar))
.Value = name;
// TODO: Consider whether you really want the *local* date, or some
// fixed time zone such as UTC
command.Parameters.Add(new SqlParameter("#RegisterDate", SqlDbType.DateTime))
.Value = DateTime.Today;
command.ExecuteNonQuery();
}
}
Try
string RegisterDate = DateTime.Now.ToString("M-d-yyyy");
and then store in database.
There is no need to manually convert date to different representation. You can go through this Custom Date and Time Format Strings. But, I agree on Jon Skeet's comment below this answer:
If you want to represent a date/time type, use a date/time type. That
way you're able to take advantage of all kinds of things that the
database can do with date/time values, and you'll never get any
non-date/time values in that field.
Note:
DateTime type uses the Gregorian calendar as their default calendar. So, as pointed out by Jon Skeet, this answer won't work with other calenders(Non-Gregorian calendars).

Trying to insert DateTime.Now into Date/Time field gives "Data type mismatch" error

If I try to write a datetime to a record in an MS-Access database the easy way, like this
cmd.CommandText = "INSERT INTO [table] ([date]) VALUES (?)";
cmd.Parameters.AddWithValue("?", DateTime.Now);
I get an exception saying "Data type mismatch in criteria expression."
Can anybody tell me why? What goes wrong here?
After a little experimentation, I found that I can make it work if I write
OleDbParameter parm = new OleDbParameter("?", OleDbType.Date);
parm.Value = DateTime.Now;
cmd.Parameters.Add(parm);
but doing it like this seems less neat, less straightforward. Why is this necessary? Am I overlooking something simple?
The problem of the mismatch in criteria expression is due to the OleDbType assigned to the parameter used to represent the DateTime.Now value when you call AddWithValue.
The OleDbType choosen by AddWithValue is DBTimeStamp, but Access wants a OleDbType.Date.
http://support.microsoft.com/kb/320435
Searching on the NET I have found another intersting tip.
The core problem lies in the OleDbParameter that cannot handle the milliseconds part of the DateTime.Now. Probably forcing the OleDbType to be Date the milliseconds part is omitted.
I have also found that the insert works also with the DBTimeStamp type if we remove the milliseconds from the date.
cmd.Parameters.AddWithValue("?", GetDateWithoutMilliseconds(DateTime.Now));
private DateTime GetDateWithoutMilliseconds(DateTime d)
{
return new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second);
}
oh, well, waiting for someone that explain this better.
The simplest statement asks the db engine to use its Now() function to get the current Date/Time value. Or you could use its Date() function if you aren't interested in the time of day; Date() will actually give you midnight as time of day.
INSERT INTO [table] ([date]) VALUES (Now());
IOW, you needn't bother massaging a Date/Time value in .Net in order to insert it into your Access db.
If you want an INSERT statement which includes a literal date value, use the # date delimiters. So to insert today's date:
INSERT INTO [table] ([date]) VALUES (#2013-04-25#);

How to pass datetime from c# to sql correctly?

I have a table and the date-times in it are in the format:
2011-07-01 15:17:33.357
I am taking a c# DateTime when I do a .ToString() on the object I am getting a DateTime in the format:
04/07/2011 06:06:17
I'm wondering how I correctly pass the correct DateTime through because when I run the SQL that is in our code it doesn't work (i.e. select the correct DateTime). I can't use SQL profiler.
This is the code:
//looks to if a user has had any activity in within the last time specified
public bool IsUserActivitySinceSuppliedTime(int userId, DateTime since)
{
//get everything since the datetime specified [usually 5 hours as this is
//how long the session lasts for
string sql = "SELECT * FROM tbl_webLogging WHERE userid = #userid AND DateAdded > #sinceDateTime";
SqlParameter sinceDateTimeParam = new SqlParameter("#sinceDateTime", SqlDbType.DateTime);
sinceDateTimeParam.Value = since;
SqlCommand command = new SqlCommand(sql);
command.Parameters.AddWithValue("#userid", userId);
command.Parameters.Add(sinceDateTimeParam);
using (SqlDataReader DataReader = GetDataReader(command))
{
if (DataReader.HasRows)
{
return true;
}
else
{
return false;
}
}
}
UPDATE*******************
I have run the following on the data:
SELECT * FROM tbl_webLogging
WHERE userid = 1
AND DateAdded > '2011-07-01 07:19:58.000'
And
SELECT * FROM tbl_webLogging
WHERE userid = 1
AND DateAdded > '04/07/2011 07:19:58'
One returns 53 records the other returns 69 records. How can this be? And when I pass the DateTime (04/07/2011 07:19:58) from c# to SQL no records show up at all!
You've already done it correctly by using a DateTime parameter with the value from the DateTime, so it should already work. Forget about ToString() - since that isn't used here.
If there is a difference, it is most likely to do with different precision between the two environments; maybe choose a rounding (seconds, maybe?) and use that. Also keep in mind UTC/local/unknown (the DB has no concept of the "kind" of date; .NET does).
I have a table and the date-times in it are in the format: 2011-07-01 15:17:33.357
Note that datetimes in the database aren't in any such format; that is just your query-client showing you white lies. It is stored as a number (and even that is an implementation detail), because humans have this odd tendency not to realise that the date you've shown is the same as 40723.6371916281. Stupid humans. By treating it simply as a "datetime" throughout, you shouldn't get any problems.
I had many issues involving C# and SqlServer. I ended up doing the following:
On SQL Server I use the DateTime column type
On c# I use the .ToString("yyyy-MM-dd HH:mm:ss") method
Also make sure that all your machines run on the same timezone.
Regarding the different result sets you get, your first example is "July First" while the second is "4th of July" ...
Also, the second example can be also interpreted as "April 7th", it depends on your server localization configuration (my solution doesn't suffer from this issue).
EDIT: hh was replaced with HH, as it doesn't seem to capture the correct hour on systems with AM/PM as opposed to systems with 24h clock. See the comments below.

Categories