Exclude time comparison from Datetime field in Dynamic LINQ Expressions - c#

I want to generate a dynamic LINQ expression for filtering only with Date, but my column is a Datetime field in the DB. Due to this the operator "equal" and "not equal" is not working because it is appending some default time to my input and trying to match with the data. If there is any way to Generate a LINQ expression that will compare only date by excluding the time.
This is my code:
// for type conversion start
var propertyType = ((PropertyInfo)propertyName.Member).PropertyType;
var converter = TypeDescriptor.GetConverter(propertyType);
if (!converter.CanConvertFrom(typeof(string)))
throw new NotSupportedException();
var propertyValue = ReturnPropertyValue(rule, converter);
var constant = Expression.Constant(propertyValue);
var valueExpression = Expression.Convert(constant, propertyType); //{Convert(5/24/2021 12:00:00 AM, DateTime)}
// for type conversion ends
// returning the expression
return Expression.Equal(propertyName, valueExpression);
// {(Param_0.CreatedDate == Convert(5/24/2021 12:00:00 AM, DateTime))}
But here I need something like this
{(Param_0.CreatedDate == Convert(5/24/2021 12:00:00 AM, Date))}
Which will exclude this time checking and will compare only with date

Don't do it; go the route suggested of using a date range instead
Always seek to avoid creating queries that manipulate table data before a comparison is done. Suppose you have a table with ten million datetimes in, and they're all indexed
The database will probably use the index for this:
WHERE datecol >= '2001-01-01' and datecol < '2001-01-02'
The database will probably not use the index for this:
WHERE CAST(datecol as DATE) = '2001-01-01'
.. so every time you query the db will fully scan either the table or the index, converting every one of all ten million values before doing the comparison

you can try the below code in C# linq:-
var data = collection.Where(t=> DbFunctions.TruncateTime(t.CreatedDate)==DbFunctions.TruncateTime(dateVariable)).ToList();
above code will exclude the time while doing field comparison in Linq.

Related

Getting Later Date from Database without counting Filtered date

Im trying to get "greater"/later date from database with query below
Controller
string str = "2019-10-27T20:44:55.323";
DateTime Date = Convert.ToDateTime(str);
var List = (from a in dbcontext.system_notification where a.notification_date > Date select a).ToList();
Output
I still got result from date "2019-10-27T20:44:55.323".
Im expecting to get only Later date from date given since im using ">" operator.
Thank You!!
The DateTime struct represents dates as a 64 bit number that measures the number of “ticks” since a particular start date. Ten million ticks equals one second.
See: https://blogs.msdn.microsoft.com/ericlippert/2010/04/08/precision-and-accuracy-of-datetime/
Now, compare this to the data types of MS SQL:
datetime accuracy: 0.00333 s [ DEFAULT IN EF ]
datetime2 accuracy: 100 ns
So, most likely, the value in your database is actually, sightly, bigger or rounded.
You can test this by checking the delta:
foreach (var dt in List)
{
var delta = Date - dt;
// check the value.
}
You can try this as below:
You should avoid using reserved words or keywords as a variable name.
string str = "2019-10-27T20:44:55.323";
DateTime dt = Convert.ToDateTime(str);
var lst = dbcontext.system_notification.where(x => x.notification_date > dt).ToList();

Comparison SQL and SQLite DateTime EQUAL values results in False C#

I Create SQLite record with DateTime.UtcNow.
Then, I convert DateTime like this:
var format = "yyyy-MM-ddTHH:mm:ss:fff";
var ModifiedString = _object.Modified.ToString(format);
var ModifiedFormatted = DateTime.ParseExact(ModifiedString, format, CultureInfo.InvariantCulture);
This value is inserted into SQL along with other record's data:
sqlCmd.Parameters.Add("#Modified", System.Data.SqlDbType.DateTime2).Value = ModifiedFormatted;
Then I query record and compare SQLite with SQL again to check whether DateTime value is the same(for testing it is the same). I get result that values are not equal:
I use Select * From TableName and:
_dbobject.Modified = Convert.ToDateTime(dr["Modified"]);
Comparison is simply like this:
if (_object.Modified < _dbobject.Modified)
{
//update local
} else if (_object.Modified > _dbobject.Modified)
{
//update online
} else
{
//do nothing
}
The local or online records were not altered in the meantime but it always results as local being > than online.
Am I missing something?
Ok, so it was due to the millisecond precision between DateTime and DateTime2, where DateTime2 has more than 3 places after second and during conversion to DateTime it was rounding up/down resulting in values not being equal.
I trimmed two dates by:
DateTime date1 = _object.Modified.AddTicks(-_object.Modified.Ticks % TimeSpan.TicksPerSecond);
and the same for the other date.
I don't result more precise than this so I am ok. However not everyone would be satisfied.

How to take a varchar field and compare as date in C# using RowFilter

I have a field in my SQL table
`OurDate (varchar(250), null)`
//sample dates
// 01/18/2018
// 05/12/2016
// 03/05/2012
I am trying to compare the table data between two dates in that field column using RowFilter:
string fromDate = "{OurDate Sql column value from dropdown list}"; //01/18/2018
string toDate = "{OurDate Sql column value from dropdown list}"; //01/18/2018 (could be different, if someone doesn't want same day)
dataTable.DefaultView.RowFilter = string.Format("(CONVERT({0}, 'System.DateTime') >= #" + Convert.ToDateTime(fromDate) + "# And {0} <= #" + Convert.ToDateTime(toDate) + "# )", "OurDate");
I get this error:
String was not recognized as a valid DateTime.
Can someone please assist me in resolving this issue.
First you should identify what causes the Conversion issue.
Is it the CONVERT expression or the values used for filtering.
You can do this by splitting up your code and adding an expression column just for the conversion like this:
dataTable.Columns.Add("TEST", typeof(DateTime), "CONVERT(OurDate, 'System.DateTime')");
If this line causes the error - it means that the problem is converting the data. DataExpression's CONVERT function should be based on the dataTable.Locale (which defaults to CultureInfo.CurrentCulture) property.
If your data is indeed always in "MM/dd/yyyy" format setting this to
dataTable.Locale = new CultureInfo("en-US");
Should solve the problem - If the data in the table is in an inconsistent format - you will not be able to solve this using RowFilter/DataExpressions
At this point you will notice that your second filter condition is missing the CONVERT entirely.
In fact this entire convert step is superficial if all of your data is in the correct format - if RowFilter detects its correct format for datetime - it will apply this conversion automatically to your column.
That being said .RowFilter only accepts dates in the following formats
American "MM/dd/yyyy" and "yyyy/MM/dd" (which I guess is considered Invariant here?)
If your local format is not one of these calling Convert.ToDateTime(fromDate) will cause a failure to convert.
Since your strings are already in one of the accepted formats - calling this should just be a pointless round trip anyway.
TL;DR: assuming all your formats are correct this should work:
dataTable.DefaultView.RowFilter = $"OurDate >= #{fromDate}# And OurDate <= #{toDate}#";
If your rows have different formats you'll need something with more control - I'd recommend LINQ to DataSet as the easiest way out since you'll be able to return DataViews/DataTables with it.
Try this instead of ConvertTo:
DateTime.ParseExact(myStr, "MM/dd/yyyy", CultureInfo.InvariantCulture);
I found it here How convert string to Datetime by a format? and changed the syntax a little to fit your db values' format.
Also you can use like that;
i think this way more readable more than rowfilter;
static void Main(string[] args)
{
string givenDateString = "01/26/2018";
DateTime convertedDateFromString = new DateTime();
DateTime.TryParse(givenDateString, out convertedDateFromString);
DataTable dt = new DataTable();
dt.Columns.Add("OurDate", typeof(System.DateTime));
DataRow dR = dt.NewRow();
dR["OurDate"] = DateTime.Now;
dt.Rows.Add(dR);
//way 1
var result = from d in dt.AsEnumerable().ToList()
where d.Field<DateTime>("OurDate").Date == convertedDateFromString.Date
select d;
Console.WriteLine(result.FirstOrDefault()["OurDate"].ToString());
//way 2
var result2 = from d in dt.AsEnumerable()
where d.Field<DateTime>("OurDate").Date == convertedDateFromString.Date
select d;
//result.FirstOrDefault<DataRow>[""];
Console.WriteLine(result2.FirstOrDefault<DataRow>()["OurDate"].ToString());
}

UTC in DB confusion when querying in linq from Web API server

I can really use some help wrapping my head around a problem I'm having querying data according to a SQL Date field.
I am storing the Date in UTC format using the following code:
objFitCalendarDto.Day = objFitCalendarDto.Day.ToUniversalTime();
That line assigns the date to the model that is inserted into the db through Entity Framework.
Now, my query is supposed to retrieve a row based on a date. So, I should be able to get the row for today, tomorrow, yesterday, and so on.
To do this, I'm using the method to search between two dates, a start date and an end date as follows:
DateTime dayBegin = DateTime.Today.Date.AddDays(dayOffset);
DateTime dayEnd = DateTime.Today.Date.AddDays(dayOffset + 1);
The purpose of dayOffset is to specify which day. If Offset is 0, then I am searching for Today. If dayOffset is 1, then I am searching for rows with tomorrow's date.
Now, since I stored the data originally in UTC, I am assuming that I must search for it in UTC as well. So before executing my query, I convert the dates to UTC like so:
dayBegin = TimeZoneInfo.ConvertTimeToUtc(dayBegin);
dayEnd = TimeZoneInfo.ConvertTimeToUtc(dayEnd);
Then I execute my query like so:
var query = (from f in Db.FitCalendars
where f.FitProgramId == programId &&
f.DayAsDate >= dayBegin && f.DayAsDate < dayEnd
select f);
problem is, it doesn't work. I have a row with the date, "2016-01-26" when I look at it in SQL Manager. However, it only returns from a query on yesterday's date. Today is 2016-01-26, by the way. Clearly I'm not getting this UTC concept. Can anyone see what I'm doing wrong here? I was assuming that if I stored everything as UTC and then before querying I converted my dates for the query to UTC, that everything should work.
UPDATE
Let's try like this:
As soon as you are storing only date part (SQL 'date' type), you
need to compare also only dates.
Instead of
DateTime dayBegin = DateTime.Today.Date.AddDays(dayOffset);
dayBegin = TimeZoneInfo.ConvertTimeToUtc(dayBegin);
let's just do
DateTime dayBegin = DateTime.UtcNow.Date.AddDays(dayOffset);
dayBegin in that case will be date with time anyway (time is 12:00:00 AM). It means, we need to truncate it with DbFunctions. We need equality check here.
var query = (from f in Db.FitCalendars
where f.FitProgramId == programId &&
f.DayAsDate == DbFunctions.TruncateTime(dayBegin)
select f);
END OF UPDATE
I believe that problem is that you comparing dates with times. In your case you need to compare only dates, as far as I understand. As a solution - use DbFunctions TruncateTime function. It can be used within linq queries - like in your code.
https://msdn.microsoft.com/en-us/library/system.data.entity.dbfunctions.truncatetime(v=vs.113).aspx
So, complete solution would be
var query = (from f in Db.FitCalendars
where f.FitProgramId == programId &&
DbFunctions.TruncateTime(f.DayAsDate) >= DbFunctions.TruncateTime(dayBegin) && DbFunctions.TruncateTime(f.DayAsDate) < DbFunctions.TruncateTime(dayEnd)
select f);

convert a string to datetime in LinqToEntities (inside the query)

I have a date in the string format in a table and I need to compare it with the parameter to retrieve all rows that match with the given date. how can I do this? I can't use DateTime.Parse and Convert.ToDateTime. both of them are not supported (I get an error). Is this possible at all in Linq OR do I have to write a stored procedure for it due to this limitation?
Note: I can't change the datatype of the column from varchar to DateTime, because that column contains other values as well, not just dates. it is a generic table and the column may contain different types of values based on the values in other columns. changing this design is outside the scope of this question.
sample code ("x" is the date parameter passed into this method):
from t1 in Table1
where EntityFunctions.DiffDays(DateTime.Parse(t1.Value),x) == 0
select new {t1.Col1, t1.Col2};
Update: if I use DateTime.Parse, I get the error "LINQ to Entities does not recognize the method 'System.DateTime Parse(System.String)' method, and this method cannot be translated into a store expression." similar error for Convert.ToDateTime as well.
Apart from the issues that #Douglas points about work with string representations, you can convert a string to DateTime in Linq to Entities using SqlFunctions and DbFunctions classes:
DbFunctions.CreateDateTime(SqlFunctions.DatePart("yy", dateString),
SqlFunctions.DatePart("mm", dateString),
SqlFunctions.DatePart("dd", dateString),
SqlFunctions.DatePart("hh", dateString),
SqlFunctions.DatePart("mi", dateString),
SqlFunctions.DatePart("ss", dateString));
(Replicating my comment to paste some sample code)
If the date is in string format, couldn’t you apply a ToString on your DateTime (presumably x) and then do a string comparison?
Since you are working on string representations, you need to take care of several issues that would otherwise be handled transparently by DateTime, including:
Date-time format discrepancies (dd/MM/yyyy vs MM/dd/yyyy).
Presence or absence of leading zeros for single-digit days and months (e.g. 01/01/2011 vs 1/1/2001).
Two-digit or four-digit representation of years (e.g. 01/01/2011 vs 01/01/11).
Timezone offsets. For example, the date for 2011-01-01 23:30 -01:00 would actually be 2011-01-02.
The sample code below will work if all your dates are in US format, with two-digit days and months, four-digit years, and no timezone offsets.
from t1 in Table1
where t1.Value.StartsWith(x.ToString(#"MM\/dd\/yyyy"))
select new {t1.Col1, t1.Col2};
Edit: Alternate solution:
If you can define a view in your database, you can circumvent the problem by casting your VARCHAR to DATETIME selectively. I’m assuming that Value is the name of your date column.
SELECT CONVERT(DATE, Value, 101) AS Value, Col1, Col2
FROM Table1
WHERE ISDATE(Value) = 1
Then, in your LINQ, do a simple DateTime equality check:
from t1 in Table1
where t1.Value == x.Date
select new {t1.Col1, t1.Col2};
This works. You need an Extensionmethod to make the dateTime parsing safe. After that you can use the result of that method in the Linq query. It will fetch all rows from the table so performance wise this might be a less optimal (!) solution. It answers the question though.
void Main()
{
var stringDates = new List<string> { "2011-13-01", "2011-01-12" };
DateTime paramDate = new DateTime(2011,01,13);
var q = from stringDate in stringDates
let realdate = stringDate.SafeParse()
where realdate == paramDate
select new { stringDate, realdate };
q.Dump();
}
static class StringDateParseExt
{
public static DateTime SafeParse(this string any)
{
DateTime parsedDate;
DateTime.TryParseExact(any,
"yyyy-dd-MM",
System.Globalization.CultureInfo.InvariantCulture ,
System.Globalization.DateTimeStyles.None,
out parsedDate);
return parsedDate;
}
}
Linq to SQL does have support for Convert.ToDateTime to go from String to DateTime. I'm not sure about Entity Framework though, if you are really using that instead. There is also a class SqlMethods with a DateDiff method that you can use to get translated to TSQL's DateDiff function.
Linq to SQL will also let you convert between types by casting. You can't directly cast between String and DateTime, but you can cheat by casting to Object first and then to DateTime. The Object cast gets erased in the translate but the DateTime cast get converted to a TSQL convert operation.
Have you tried casting?
Expression<Func<string, DateTime>> expr = s => (DateTime)(object)s;
Further web searching reveals that the lack of string-date conversion is a missing feature in EF. It looks like some string conversions are supported, but not date parsing: http://msdn.microsoft.com/en-us/library/dd466166.aspx

Categories