How to pass datetime from c# to sql correctly? - c#

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.

Related

Sqlite Net PCL DateTime Precision

I need to save a datetime value with miliseconds precision - e.g.: "11/28/2019 09:59:40.777" - in the table "Journal", I am using a trigger for it.
This is my Model:
public class Journal
{
public int IdAluno { get; set; }
public int IdAulaProfessor { get; set; }
[ForeignKey(typeof(JournalOperacao))]
public int IdOperacao { get; set; }
public DateTime DataHorario { get; set; }
[ManyToOne(CascadeOperations = CascadeOperation.CascadeRead)]
public JournalOperacao JournalOperacao { get; set; }
}
The SQLiteConnection contructor:
SQLiteConnection = new SQLiteConnection(DbFilePath);
The trigger
DbConnection.Execute(#"CREATE TRIGGER IF NOT EXISTS JournalInsertTrigger
AFTER INSERT ON DiarioFalta
BEGIN
INSERT INTO Journal (IdAluno, IdAulaProfessor, IdOperacao, DataHorario) VALUES (new.IdAluno, new.IdAulaProfessor, 1, (strftime('%s', 'now', 'localtime') + 62135596800) * 10000000);
END;");
When the trigger inserts a row, the column "DataHorario" only saves the date and time, but I need the miliseconds too.
I have already tried (julianday('now') - 2440587.5)*86400.0, but the value I get is "1/1/0001 12:00:00 AM"
var journal = JournalData.GetItems();
var row = journal.First();
The value of row is
value = { DataHorario = "1/1/0001 12:00:00 AM", IdAluno = 144600, IdAulaProfessor = 1821540, IdOperacao = 1 }
After writing the initial answer, it wasn't until the end that I perceived your question is lacking sufficient detail. The very last line of the question says "the value I get is..." but you don't actually show how you are getting that value. Also, you show the C# model, but not the SQLite table definition. Ultimately to get a full solution that works correctly both within SQLite and between the C# model, you need to provide more details of how data is being read and converted between the model and the database. I hope my answer helps anyway.
As a reference see the SQLite document page for date and time functions.
Within SQL to get the full date-time string format with millisecond precision requires the complete format string using %f for fractional seconds like SS.SSS. It produces a string like '2019-11-28 10:27:27.146.:
strftime('%Y-%m-%d %H:%M:%f', 'now', 'localtime')
Because strftime() primarily returns string values, it is incorrect to apply addition or multiplication. Since SQLite has no native date-time data type, there is no automatic conversion for handling mathematics. There is also no need to manipulate the value since it already has millisecond precision in the last digits like SS.SSS.
The numeric date-time value returned by the function julianday() already contains millisecond precision. For simply storing the date/time value, there is no need to manipulate the value with multiplication or subtraction.
To get the current local time: julianday('now', 'localtime')
To get the corresponding string representation, pass the Julian numeric date/time value to the strftime() function like the following examples:
strftime('%Y-%m-%d %H:%M:%f', julianday('now', 'localtime'))
strftime('%Y-%m-%d %H:%M:%f', julianDayColumn)
strftime('%Y-%m-%d %H:%M:%f', 2458815.93573086)
The Unix epoch format (seconds since 1970-01-01) like strftime('%s', 'now', 'localtime') does NOT keep millisecond precision, only seconds precision... it records only integer seconds without any fractional part.
If I recall correctly (sorry I can't test right now), the official .Net library System.Data.Sqlite will automatically convert between .Net DateTime values and the SQLite string values using standard interfaces, however I'm not sure if this is true for the millisecond precision. Such automatic conversion might require that the table column be declared as DATETIME type.
Whatever the case, you will need to handle the dates within pure SQL differently than the conversion between SQL and .Net/C#. Within SQL (e.g. inside triggers, SQL update and insert statements, etc.) you need to use the SQLite date and time functions. Within C# and the .Net framework, you will want the value converted to the .Net DateTime type. But that conversion likely does not require the forced multiplication and subtraction--those manipulations will usually ruin the date-time values unless you already know exactly what you are doing and for what reason... not just guessing at formats.
If you are trying to following recommendations from other sites, I see various attempts at storing and retrieving ticks and Unix Epoch values, etc. Such attempts will likely ruin the ability to store and manipulate dates within SQL including triggers and will likewise prevent storing millisecond precision.
My only intention was to save a date time with miliseconds precision, but I wasn't getting it because SQLite-Net-PCL creates the column data type table DateTime(C#) as bigint(SQLite) and I was using strftime('%Y-%m-%d %H:%M:%f','now', 'localtime') which returns a string type.
Since I'm not interested in manipulating the column DateTime DataHorario I changed the model column DateTime DataHorario to string DataHorario, which fixed my case.

Convert a datetime() record to date in c# for importation into VFP database

I'm currently building a data export/import tool for pulling data into a Visual Fox Pro database from an excel or CSV document.
I believe the code to be functional, however upon execution I recieve a data type mismatch error.
After some investigation I've notice a difference between the format of the dates I'm pulling and the field I'm pushing to.
The Fox pro database is set up to take Date records, however the data i'm trying to push is in date time format (the original record is date) but as far as I'm aware c# can only natively do datetime conversion.
The code getting the date from excel is as such:
importCommand.Parameters["TENSDATE"].Value = exportReader.IsDBNull(0)
? (object) DBNull.Value
: DateTime.Parse(exportReader.GetValue(0).ToString());
Now, I've seen a lot of people use something like:
exportReader.GetValue(0).ToString("dd/MM/yyyy")
However I can't seem to get this functioning. Can someone advise me on the best way to achieve my goal.
You need to supply the type of the field when adding it to parameters. In this specific case, OdbcType.DateTime for a date field.
importCommand.Parameters.Add("#TENSDATE", OdbcType.DateTime).Value = exportReader.IsDBNull(0)
? (object) DBNull.Value
: DateTime.Parse(exportReader.GetValue(0).ToString());
If you want to parse dates which are in specific format you should use DateTime.TryParseExact method. You'll be able to pass specific format as an argument. Please refer to: https://msdn.microsoft.com/en-us/library/ms131044(v=vs.110).aspx
(Joshua Cameron-Macintosh, please close your open threads)
Despite my prior warnings, you are trying to do that the hard way, be it. VFP is a good data centric language and is clever enough to put a DateTime value into a Date or DateTime field. It is also clever enough to parse text values that denote a Date(time) - in the case of text, just like any other database or non-database parsers it does the parsing with given rules (such as using common canonical ODBC format of yyyyMMdd HH:mm:ss with no problem, or if instructed to use a format of say DMY, it knows 1/2/2000 means Feb 1st,2000 etc.). In summary here the problem is not on VFP side at all. If you use CSV, then be sure you are using ODBC canonical format for dates (same goes on with SQL Server for example). In case of Excel file, provided you have the correct data types, you can directly transfer with no additional work, particularly that DBNull trial was totally unnecessary, VFP knows DbNull.Value already.
Anyway code always talks better.
For this sample assume you have an excel file (d:\temp\ExcelImportData.xlsx) with SampleSheet sheet where you have the data columns as:
Customer ID: string
Order ID: integer
Ordered On: DateTime && where time parts were insignificant fro demo purposes
Shipped On: DateTime && Has NULL values
(You can build such a sample sheet using Northwind sample database's Orders table)
There is a VFP table (d:\temp\SampleImport.dbf) as the receiver where column information is:
CustomerId: Char(10) NOT NULL
OrderID: Int NOT NULL
OrderDate: Date NOT NULL
ShippedOn: DateTime NULL
Here is the simple read/write using a reader:
void Main()
{
var vfpConnection = #"Provider=VFPOLEDB;Data Source=D:\temp";
var xlsFileName = #"D:\temp\ExcelImportData.xlsx";
var xlsConnection = $#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source={xlsFileName};" +
"Extended Properties=\"Excel 12.0;HDR=Yes\"";
var xlsTableName = "SampleSheet$";
using (var xlsCon = new OleDbConnection(xlsConnection))
using (var vfpCon = new OleDbConnection(vfpConnection))
{
var cmdInsert = new OleDbCommand(#"insert into SampleImport
(CustomerId, OrderId, OrderDate, ShippedOn)
values
(?,?,?,?)", vfpCon);
cmdInsert.Parameters.Add("customerId", OleDbType.WChar);
cmdInsert.Parameters.Add("orderId", OleDbType.Integer);
cmdInsert.Parameters.Add("orderDate", OleDbType.Date);
cmdInsert.Parameters.Add("shippedOn", OleDbType.Date);
var readXl = new OleDbCommand($"select * from [{xlsTableName}]", xlsCon);
xlsCon.Open();
vfpCon.Open();
var xlReader = readXl.ExecuteReader();
while (xlReader.Read())
{
cmdInsert.Parameters["customerId"].Value = xlReader["Customer ID"];
cmdInsert.Parameters["orderId" ].Value = xlReader["Order ID"];
cmdInsert.Parameters["orderDate" ].Value = xlReader["Ordered On"];
cmdInsert.Parameters["shippedOn" ].Value = xlReader["Shipped On"];
cmdInsert.ExecuteNonQuery();
}
xlsCon.Close();
vfpCon.Close();
}
}

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.

Date Parameters passed to stored procedure not working as expected

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)

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).

Categories