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

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

Related

Exclude time comparison from Datetime field in Dynamic LINQ Expressions

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.

DocumentDB how to LINQ query on _TS?

I want to query the update timestamp _ts on documents to get documents that haven't mutated since a certain amount of time.
When I create a select query in the azure portal this works:
SELECT TOP 10 c.id FROM c WHERE c._ts < 6.35909919217878E+17
The wierd number is the Ticks created with a datetime object, see below.
But when I try to create it through LINQ it won't do because you don't have _ts but a Timestamp as a DateTime object. When I try to enter a full DateTime object to compare to the Timestamp it crashes saying it doesn't support it. So I try this:
DocRepo.Get(x => x.Timestamp.Ticks < CloseDate.Ticks);
This results to nothing and when I watch the query executed it has this as a select query:
SELECT * FROM root WHERE root[\"_ts\"][\"Ticks\"] < 6.35909943137688E+17
Is it possible to query on the _ts timestamp or do i have to have an extra updatedAt field to do it, which seems redundant.
You have a couple of problems with your queries. In your first query, you are comparing "Ticks" (one ten millionth of a second - see here) to the _ts value which will most likely return all the documents in your collection because the _ts value is a POSIX (Unix) time measured in seconds see here. They also aren't based on the same epoch. The Unix value starts at midnight 1,1,1970 where the Ticks start at midnight 1,1,0001 Therefore, the _ts value will always be much smaller than the Ticks value (not to mention off by 1,969 years!). You will need to convert your dates to their Unix time value. You could create an Extension method to help you do this:
public static long ToUnixTime(this DateTime date)
{
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return (long)(date - epoch).TotalSeconds;
}
As for the Linq statement, you can't (unfortunately) put a DateTime into a Linq query because a DateTime value won't convert to a const (which is the error you are getting). So, in both cases, you can't compare either the _ts value or TimeStamp value very easily.
So what to do? Well, in looking at the DocumentDB SDK, if you look at the definition of TimeStamp you will see the following:
// Summary:
// Gets the last modified timestamp associated with the resource.
[JsonConverter(typeof(UnixDateTimeConverter))]
[JsonProperty(PropertyName = "_ts")]
public virtual DateTime Timestamp { get; internal set; }
So by default, the SDK is converting the _ts value to a DateTime and exposing it through TimeStamp field. There are a couple of things you could do depending on what type your DocRepo is returning. If it is the default Document type, you could create a new class and inherit from the Docment type like this:
public class MyDocument : Document
{
public long _ts
{
get; set;
}
}
If it is your own custom class, then just add the _ts field to your class. Either way, if the _ts field is present, DocumentDB will populate the field. Then, if you add the ToUnixTime extension method you could compose your Linq query like this:
DocRepo.Get(x => x._ts < CloseDate.ToUnixTime());
It may not be an elegant solution and someone (hopefully) might come up with a better solution, but I have verified that it works against my own DocumentDB collection.
Hope this helps.
This is what i use to convert Unix timestamp into DateTime format we can easily understand in C#:
public DateTime TimeStamp
{
get
{
return DateTimeOffset.FromUnixTimeSeconds(int.Parse(_ts)).DateTime;
}
}
Hope this helps.

Mysql DB returns wrong date format (C#)

This is a very simple question about a confusing circumstance with my C#-App:
Usings:
C# Desktop-App / Mysql 5.5
Whats going on:
My app is selection some data from the db (Select date from mytable). The format for column date is yyyy-mm-dd. After retrieving the data inside my C#-App im getting a culture-sensitive output. In my case in format dd.mm.yyyy. Check out my screenshot from debugging.
Notice: The var listDate is from type string!
My output looks like from type DateTime. But I've never converted it into dateTime. The column(db) date is set to format date.
So why am I getting this format? Is it a db or app problem?
My goal is to get the same format yyyy-mm-dd as inside the db.
Every help appreciated. Thanks very much!!!
EDIT 1:
My code:
public static void DB_Select(string s, params List<string>[] lists)
{
try
{
using(var conn = new MySqlConnection(ServerConnection))
{
conn.Open();
MySqlCommand cmd = conn.CreateCommand();
cmd.CommandType = CommandType.Text;
string command = s;
cmd.CommandText = command;
using(var sqlreader = cmd.ExecuteReader())
while (sqlreader.Read())
{
if (sqlreader[0].ToString().Length > 0)
{
for (int i = 0; i < lists.Count(); i++)
{
lists[i].Add(sqlreader[i].ToString());
}
}
else
{
foreach (List<string> save in lists)
{
save.Add("/");
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show("Error while selecting data from database!\nDetails: " + ex);
}
}
Cant imagine this could be the problem. All this is doing is looping through the output and saving the single columns into different passed lists (params List<string>[])
Using:
Services.DB_Select("SELECT date FROM myTable WHERE something = something")
So everything is kept into strings without any converts. It is only about the select and the output db -> c#. Im selecting a date-format and getting a dateTime value culture-sensitive. How is this possible?
EDIT 2: (SOLUTION)
If I get it like SELECT CONCAT(date) FROM mytable... (telling the db to handle (col)date as a string I get the clean output yyyy-mm-dd !
Without concat I get my dateTime output culture-sensitive. So it has to be a problem with my date-format for the date column. But why - really dont know.
If you want, you can get it like this:
string example = "20.08.2013 00:00:00";
DateTime result = GetDate(example);
By using this method:
public static DateTime GetDate(string stringFormat)
{
var date = stringFormat.Split(' ')[0].Split('.').Select(x => Int32.Parse(x)).ToArray();
return new DateTime(date[2], date[1], date[0]);
}
Since you did not provide how you get the data from DB it's hard to say. There are probably two options - 1) some kind of implicit conversion done when retrieving data, so you get the formatted string from db or 2) you do the conversion somewhere after you get the data.
What you could do is conversion to DateTime
listDate.Select(d => DateTime.ParseExact(d,"dd.MM.yyyy hh:mm:ss",CultureInfo.InvariantCulture)) and then format it back to format that you need.
Better solution would of course be getting the date in DateTime directly from DB. Please post your code for this.
EDIT
The problem there is that sqlreader[i].ToString() - this takes your Culture from Environment and does the conversion. In that case I would check for type of the value and if that is DateTime then use ToString("yyyy-MM-dd") instead of simple ToString()
From documentation:
This method uses formatting information derived from the current
culture. In particular, it combines the custom format strings returned
by the ShortDatePattern and LongTimePattern properties of the
DateTimeFormatInfo object returned by the
Thread.CurrentThread.CurrentCulture.DateTimeFormat property. For more
information, see CultureInfo.CurrentCulture. Other overloads of the
ToString method enable you to specify the culture whose formatting to
use and to define the output pattern of the DateTime value.
Alright, a college of mine told me a solution. At least it sounds logic to me:
The col itself is from type date which is a short version of dateTime. When using a clear select like SELECT date FROM mytable the output will come as a DateTime. Thats why I also get the time-part inside my output.
When going for SELECT CONCAT(date) FROM mytable the value is handled like a string. A workaround I found myself. This will only output the date itself.
Strange behaviour. For sure because I work with this date-formatted-column for a long time. Never had any problems with it.
Thanks a lot for your interest!

C# MySQL LINQ DateTime conversion

I'm trying to retrieve records from a mySQL DB using LINQ and C#.
The date in c# code is a string: 23-01-2010
I need to convert this to DateTime format as 2010-01-23 (mySQL default DateTime format), otherwise the query does not return any records, at present it errors saying a string cannot be matched against a DateTime (row.DateOfIssue)
If I convert the string to DateTime (C#), then it is not in the mySQL DateTime format of yyyy-MM-dd
String endDate = "23-01-2010";
var query = (from row in uow.UserPersonalLicenseDetails
where (endDate >= row.DateOfIssue && endDate <= row.DateOfExpiry)
select row)
This is such a standard query it seems mad that it is so hard to do in LINQ.
It seems putting any method like CompareTo etc in the where clause causes an error of "Search for a class that works in this scenario"
I'm now wondering if the best line of attack might be to write a stored procedure in the database. This could then take the C# datetime as a parameter, convert it to mySQL format and then run the required query.....
Any thoughts?
Make it a DateTime - so
var myDate = DateTime.Parse(endDate);
Then
myDate.ToString("yyyy-MM-dd");
---------- or this:
var myDate = DateTime.Parse(endDate);
var query = (from row in uow.UserPersonalLicenseDetails
where ((myDate.CompareTo(row.DateOfIssue)>=0 && (myDate.CompareTo(row.DateOfExpiry)<=0)
select row
Just convert your date string to DateTime and then in the LINQ convert the string to DateTime that's coming back in order to do the comparison. I've used ParseExact because we need to make sure that we are parsing to the exact format that MySQL stores the date in, which is yyyy-MM-dd hh:mm.
Something like:
var endDate = DateTime.Parse("23-10-2010").ToString("yyyy-MM-dd hh:mm");
var formattedEndDate = DateTime.Parse(endDate);
//you could cut that to one line by putting the date in said format in the first place
var query = (from row in uow.UserPersonalLicenseDetails
where formattedEndDate >= row.DateOfIssue
&& formattedEndDate <= row.DateOfExpiry
select row)
Ok, problem was that the Table had the field defined as DATE rather than DATETIME so no match was being made.
Used DateTime.Date and the match was made.

Working with nullable DateTime

I am using LINQ and have a few properties thats DateTime? type.
If i now want to add the value from a textbox i cant seem to get this to work.
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ScoringLastUpgrade", DbType="Date")]
public System.Nullable<System.DateTime> ScoringLastUpgrade
The textbox i use i have made sure with javascript that the format will be '2011-06-17'
But now when i try to do this:
myObject.ScoringLastUpgrade = Convert.ToDateTime(txtScoringUpgradeDate.Text).ToShortDateString();
I get this error: "Cannot convert type string to DateTime?"
How to do this?
The .ToShortDateString() call is converting it into a string. You should remove that call.
Also, you say you've made sure of the format with javascript. What if the user doesn't have javascript, you should do server-side checks too. Also, since it's in a given format, you can use DateTime.ParseExact or DateTime.TryParseExact (so an invalid format doesn't throw an exception) since its more efficient. (The format string would be "yyyy-MM-dd") i believe.
Don't convert it to string using 'ToShortDateTimeString', just set the result of Convert.ToDateTime:
myObject.ScoringLastUpgrade = Convert.ToDateTime(txtScoringUpgradeDate.Text);
Assuming you've done sufficient validation on txtScoringUpgradeDate.Text?
My preference when dealing with these type conversions is to use the TryParse method, e.g.:
DateTime date;
if (DateTime.TryParse(txtScoringUpgradeDate.Text, out date))
myObject.ScoringLastUpgrade = date;
The Convert.ToDateTime, much like the explicit DateTime.Parse will throw an InvalidCastException when an excepional value occurs. It's better to make your code fault tollerant then needlesly catch an exception.
UPDATE: based on your last comment:
You shouldn't return DateTime.MinValue in this case, as MinValue is less than the supported min value of a datetime column. the CLR DateTime supports a date range down to 0000-01-01, whereas the SQL datetime (as well as the comparative CLR SqlDateTime type) supports a minimum value of 1753-01-01. As it as a nullable DateTime, you should set it to null:
public static DateTime? ToNullableDateTime(this string date)
{
DateTime dateTime;
return (DateTime.TryParse(date, out dateTime))
? (DateTime?)dateTime
: null;
}
The problem is that you have put ToShortDateString() at the end there, effectively converting the DateTime back to a string again. Try removing that part of the line.
In my case (.cshtml):
<td>#item.InvoiceDate.Value.ToShortDateString().ToString()</td>
Where InvoiceDtae is Nullable Date in DB

Categories