How to compare to datetime values to millisecond accuracy in LINQ query - c#

I'm running a fairly straight forward LINQ query that looks like the following:
List<Event> lstEvents = db.Events.Where(e => e.SystemDatetime <= eventItem.SystemDatetime).ToList();
The SystemDateTime is a DateTime column in the database that stores datetimes in the format yyyy-mm-dd hh:MM:ss.fff. DateTime comparisons only compare to the nearest second which isn't accurate enough.
How do I compare the two values in the LINQ statement to the millisecond?

You can retrieve the total Milliseconds of a DateTime object in C#. Your existing comparison would then work with the higher accuracy.
List<Event> lstEvents = db.Events.Where(e => e.SystemDatetime.TotalMilliseconds <= eventItem.SystemDatetime.TotalMilliseconds).ToList();

DateTime comparisons only compare to the nearest second which isn't accurate enough.
This isn't true. It's depending on your data type of your DateTime column. Choose the datetime2 data type for more accuracy.
See the SQL below.
DECLARE #datetimes TABLE ([Column1] DATETIME2(7), [Column2] DATETIME2(7))
INSERT INTO
#datetimes
VALUES
('2019-03-26 20:00:00.123', '2019-03-26 20:00:00.124')
SELECT
*
FROM
#datetimes
WHERE
[Column1] <> [Column2]
SELECT
*
FROM
#datetimes
WHERE
[Column1] = [Column2]

Related

Using sql to add to dates during a query

I have a table that keeps track of when particular events occur, and how long they last. For reasons I cannot fathom, it was designed to store the start date of the event, start time of the event, then the number of hours and minutes the event lasted. Like this:
EventStartDate | EventStartTime | TimeSpentHours | TimeSpentMinutes
Where EventStartDate is a dateTime with the hours/minutes always set to zero, so that, even though it's a date time, all the values are like "12/22/2016 00:00". The EventStartTime is a char(4) which is military time of the start of the event. TimeSpentHours is and int which is the total hours the event duration, and TimeSpentMinutes is an int for the number of minutes. Obviously the total time spent for the event is the hours plus the minutes.
The problem: I need to be able to, given a particular DateTime, find all the events that were occuring during that time. Put another way, given a particular DateTime I need to get all the events with a starting date and time that's greater than or equal to the given DateTime and less than or equal to an "end" date and time.
So I need to compute the "EndDateTime" based off the values in the database during the query. The database is SqlServer 2008 R2. I am using C# for WinForm application to query the data.
So far I have roughly:
public static List<ImportantEvents> GetEventsDuringDateTime(DateTime timeOfEvent)
{
using (SqlConnection sqlConn = getAndOpenSqlConn())
{
string theEventTime = timeOfEvent.ToString("hhmm");
string sqlStmt = "SELECT EVENT_ID, AGENCY, EVENTSTARTDATE, ACTNOTE, EVENTSTARTTIME, TIMESPENTHOURS, TIMESPENTMINUTES FROM EVENTSMAIN WHERE((EVENTSTARTDATE<= #MYEVENTDATETIME AND EVENTSTART TIME< #ACTTIME) AND ...";"
}
}
(the above SQL obviously won't work and is where I am stuck...)
My question is: how can I, in the query, add the EVENTSTARTTIME to the EVENTSTARTDATE to create a new "temporary" column, then add the TIMESPENTHOURS and TIMESPENTMINUTES to that column into another new "temporary" column, to then query against given a specific DateTime value???
It is possible to achieve this in a single query with a common-table expression like this:
With StartAndEndTimes As (
Select Event_ID,
EventStart = DateAdd(Minute, Convert(int, Right(EventStartTime, 2)), DateAdd(Hour, Convert(int, Left(EventStartTime, 2)), EventStartDate)),
EventEnd = DateAdd(Minute, Convert(int, Right(EventStartTime, 2))+TimeSpentMinutes, DateAdd(Hour, Convert(int, Left(EventStartTime, 2))+TimeSpentHours, EventStartDate))
From EventsMain)
Select Event_Id, EventStart, EventEnd, <<add other fields here>>
From StartAndEndTimes
Where EventStart <= #MyEventDateTime
And EventEnd > #MyEventDateTime;
Basically you can extract the hours and minutes from the start time and add them to the start date to get a true, datetime, start date. Similar with the end date. It is not necessary to use common-table expression here, but it does make the code more readable. Then you just do the ordinary date comparison to your input parameter.
Here I have disected parts of the final query. You will need to put the final part into your query wherever you need it.
SELECT Combined = EVENTSTARTDATE + EVENTSTARTTIMEFROM FROM EventsMain
SELECT CombinedWithHour = DATEADD(hh, TIMESPENTHOURS, Combined) FROM EventsMain
SELECT CombinedWithMinute = DATEADD(mi, TIMESPENTMINUTES, CombinedWithHour) FROM EventsMain
All together:
SELECT DATEADD(mi, TIMESPENTMINUTES, DATEADD(hh, TIMESPENTHOURS, EVENTSTARTDATE + EVENTSTARTTIME)) FROM EventsMain

RowFilter expression of DataTable for subtracting two dates

Can anyone correct my RowFilter expression to subtract two dates where one date is database table column and other date is current date. Below is my code snippet where i am checking whether difference between the dates is less than or equal to 10."CreationDateTime" is database table column.With this code i am getting missing operand error.
dataQuery = string.Format("(CreationDateTime - (" + DateTime.Now + "))<={0}",10);
myDataView.RowFilter = dataQuery;
One option might be create another column (creating another column in your query) for the difference hours between them and creating a filter after that.
You can use DATEDIFF to get's difference current date and return int as an hour like;
DATEDIFF(hh, CreationDateTime, GETDATE()) as HourDiff
and you can filter that in your DataTable like;
myDataView.RowFilter = "HourDiff <= 10";

Linq datetime compare in where clause

I have a table with a couple of columns called Key and Value.
One of the Keys is called RequestedOn and the value is a timestamp saved as a string.
Now in my linq query I would like to compare this timestamp, for example
var startDate = DateTime.Now.AddDays(-7);
var endDate = DateTime.Now;
var query = (from ep in db.ExtendedProperties
where
ep.Key == "RequestedOn" && ep.Value >= startDate && ep.Value <= endDate
select ep).ToList();
Now I know I cannot compare ep.Value (string) against startDate or endDate, and I cannot convert ep.Value to a DateTime as it couldn't make sense. Any ideas on a solution?
Thanks in advance?
if its saved as string you must parse it to DateTime and then compare
You can either:
Convert the DateTime to string
Convert the string to DateTime
Write a method that compares the 2 directly
Something else
Using LINQ for something that does not have a type and contains different data is not a good option. The whole point of LINQ is to have types.
That being said one solution to your problem is to save the date in a format that is comparable as string such as 201405231801 (being 23.05.2014 18:01) and just compare strings.
Of course you care about timezones and daylight saving time you are guaranteed to get it wrong.
If you save your strings right you can actually do a comparison:
For example: '20131201125959': year, month, date, hours, minutes, seconds.

SQLite scalar function return type always string?

If I select a date-typed column in sqlite without any functions, it returns me a proper .NET DateTime object:
// Returns .NET DateTime object
select date_column from test_table;
But the moment I use a scalar function, it always returns me a string:
// Returns string
select datetime(date_column) from test_table;
select date(date_column) from test_table;
The reason I need this is for date rounding
// Rounds to month
select datetime(strftime('%Y-%M-01T00:00:00', date_column)) from test_table;
// Rounds to year
select datetime(strftime('%Y-01-01T00:00:00', date_column)) from test_table;
I tried implementing my own SQLiteFunction, but it still returns a string:
[SQLiteFunction(Name="round_date")]
public class RoundDate : SQLiteFunction
{
/// <summary>
/// Expects 2 arguments:
/// 1: date
/// 2: rounding (Month, Quarter, Year)
/// </summary>
public override object Invoke(object[] args)
{
DateTime date = SQLiteConvert.ToDateTime(args[0].ToString());
DateTime result = date;
switch (args[1].ToString())
{
case "Month":
result = new DateTime(date.Year, date.Month, 1);
break;
case "Quarter":
int quarter = (date.Month-1)/3;
var quarterStart = quarter*3 + 1;
result = new DateTime(date.Year, quarterStart, 1);
break;
case "Year":
result = new DateTime(date.Year, 1, 1);
break;
}
// function returns a DateTime object.
// But executing the statement returns a string
return result;
}
}
// Uses custom function, but still getting String
select round_date(date_column, 'Year') from test_table;
I know I can typecast/convert the string result manually.
But is there any way to force the select statement to return a DateTime object automatically?
[Edit 1]
I understand that Dates and DateTimes are not one of the natively-supported data types in sqlite.
I also know that I can manually convert the formatted strings to DateTime objects.
However, select date_column from test_table actually returns .NET DateTime objects. I assume the System.Data.SQLite implementation does this conversion automatically.
var selectStmt = sqlite.CreateCommand();
selectStmt.CommandText = "select date_column from test_table";
// ==============================
// ==============================
// Returns System.DateTime object
var this_is_a_DateTime_object = selectStmt.ExecuteScalar();
// ==============================
So I was expecting this same behaviour when using date(), datetime(), and my own SQLiteFunction implementation.
I don't believe there's a way to force the System.Data.Sqlite to automatically return the .NET DateTime datatype for you in this instance. Here's what's happening. Let's say your table definition looks like this:
CREATE TABLE test_table (
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
date_column DATETIME NOT NULL
)
As you already noted, there's really no such thing as a data type in SQLite. But, as you also noted, the library is clearly handing you a native DateTime value back in this instance. My guess is the same as yours: it's using the semi-arbitrary wording of DATETIME as your request to undertake this helpful conversion on your behalf.
To test this, I added a clearly invalid date into one of my DATETIME columns and the library threw the exception "String was not recognized as a valid DateTime".
Now, to answer your question of why you don't get this same benefit using one of the date/time functions, the answer lies in the documentation:
Notice that all other date and time functions can be expressed in terms of strftime():
date(...) strftime('%Y-%m-%d', ...)
time(...) strftime('%H:%M:%S', ...)
datetime(...) strftime('%Y-%m-%d %H:%M:%S', ...)
julianday(...) strftime('%J', ...)
The only reasons for providing functions other than strftime() is for convenience and for efficiency.
So you're not really calling date/time functions. You're calling a formatting function whose return time is--you guessed it--a string. I made a few failed attempts myself at trying to cast the result of a function back to a datetime, like so:
select (date(...) as DATETIME), ...
But unfortunately, you can only typecast to one of SQLite's native storage classes using the type affinity name (one of: NONE, TEXT, REAL, INTEGER, or NUMERIC).
So it looks like you're back to having to take care of the conversion yourself after the fact. (As will I.)
As stated before, you can build a table with DATETIME type for a column, which returns a DateTime datatype in .NET.
Once you use any of the internal functions or Custom SQLiteFunctions it reverts it to an internal (non-date) data type.
There is a funny hack that can be used if you really want to avoid the String data type, this appears to be the ONLY way I can force it to return a DateTime data type in .NET after some level of date manipulation (so far). And I'm not really keen on it myself...
If you define a table like such...
CREATE Table test2(id bigint, name nvarchar(500), DT datetime)
The query result of the third column is a DateTime.
So following the below logic (again this is ugly, but maybe will inspire someone with more ideas) will return a date time that has been modified by the internal datetime function...
CREATE TABLE output (id bigint, name nvarchar, dt datetime)
;
INSERT INTO output
SELECT id, name, DateTime(dt,'+5 years') FROM Test2
;
SELECT * FROM output
;
DROP TABLE output
Executing this as a single statement will fill one datatable with the result as expected DateTime Datatype in column 3, shifted by 5 years.
In your code if you can use a unique output table name for each call then the approach is safe from conflict with multiple statements.
In SQLite databases there are no storage data type for "Date and time"
You can convert the datatype after a SELECT command to standard .Net DateTime format.
Query : "SELECT ID, DateModified FROM Customer"
string id = reader.GetInt64(0).ToString();
DateTime dateTime = (DateTime)reader[1];
SOURCE: http://techreadme.blogspot.de/2012/11/sqlite-read-write-datetime-values-using.html
I'm sorry the above information applies to SQLite versions 1 & 2. SQLite3 does include datatypes although they are implemented differently than Standard SQL Databases.
https://sqlite.org/datatype3.html
I don't use SQLite but, from what I've read, there is no native data type for dates and times. If your original query is producing a result set that the SQLite ADO.NET provider interprets as .NET DateTime values then I can only assume that it is based on format. In that case, you might be able to use strftime to output in that same format and get the data as DateTime values automatically. It appears that that format may be "yyyy-MM-dd HH:mm:ss.fff" in .NET terms.

Getting days between two dates with IQueryable

I have an IQueryable query which I need to use sometimes in the same method.
This query is based on another one which is passed as parameter.
I need to sum the result of a value multiplied by the number of days between two dates.
parameter query = IQueryable lista;
IQueryable<ChildEntity> query = lista.SelectMany(s => s.ChildEntities).Where(w=>w.IsActive.Equals("Y");
DateTime maxDate = lista.Max(m => m.Date);
decimal value = query.Sum(s => (s.Value) * (maxDate - s.ParentEntity.Date).Days);
which gives the exception:
Specified method is not supported.
I've also tried:
decimal value = query.Sum(s => (s.Value) * SqlMethods.DateDiffDay(maxDate, s.Parent.Date);
tried also SqlFunctions.DateDiff and EntityFunctions.DiffDays and all of these last three gives me this exception:
Method 'System.Nullable`1[System.Int32]
DateDiffDay(System.Nullable`1[System.DateTime], System.Nullable`1[System.DateTime])'
is not supported for execution as SQL.
I do not want to use Enumerable because this can result in a huge number of records.
Is there any other way to find a solution for this?
(by the way, I'm using Devart as provider.)
int days = 0;
DateTime today = DateTime.Now;
DateTime maxDate = lista.Max(m => m.Date);
While (today <= maxDate)
{
today.AddDays(7);
days++
}
Your problem is that the database you are running the query on doesn't have a time-span data type like net does, however most databases store a date as number of days since a official start date, this means that if you convert the dates to a number you can just do straight arithmetic on it
days = ((int)maxdate) - ((int)currentdate)
NOTE:thats sudo not runnable

Categories

Resources