Comparing Datetime? inside Linq Expression - c#

i am trying to return queries from a DB using Entity Framework and Linq on a Nullable DateTime field.
I have the following code;
DateTime? from = new DateTime(year, month, 1);
DateTime? to = from.Value.AddMonths(1);
return context.Where<Job>(
x =>
x.NextDueDate?.Date >= from?.Date &&
x.NextDueDate?.Date <= to?.Date).ToList();
Where Job is (reduced for brevity);
public class Job : AuditableEntity
{
...
public DateTime? NextDueDate { get; set; }
I tried this from the following stack link;
Linq expression with nullable DateTime
However, when I try this I am getting the following error;
An expression tree lambda may not contain a null propagating operator
Can anyone explain a solution to this please?

As the exception message said you cannot use ?.
// First day of a month. E.g. 2/1/2018 12:00:00 AM
DateTime from = new DateTime(year, month, 1);
// Last day of a month and end of that day. E.g. 2/28/2018 11:59:59 PM
DateTime to = from.AddMonths(1).AddTicks(-1);
return context.Where<Job>(
x =>
x.NextDueDate != null &&
x.NextDueDate >= from &&
x.NextDueDate <= to).ToList();

You are setting public DateTime? NextDueDate { get; set; }. So the default value won't be null.
Please try below code, because the date time value won't be null in code even when you fetch the null date time column from db. Which have a default value equal to 1/1/0001 12:00:00 AM.
DateTime from = new DateTime(year, month, 1);
DateTime to = from.Value.AddMonths(1);
DateTime dateTimeDefault = default(DateTime);
return context.Where<Job>(
x =>
x.NextDueDate.Date != dateTimeDefault
x.NextDueDate.Date >= from.Date &&
x.NextDueDate.Date <= to.Date).ToList();

Related

Operator >= cannot be applied to operands of type string and datetime

The user enters two parameters in the url which are the start date and end date and they are entered in the format yyyyMMddhhmm as a string. I'm attempting to take these strings and turn them into dates so I can query my database.
[ResponseType(typeof(Detail))]
public IHttpActionResult GetDetail(string StartDate, string EndDate)
{
DateTime StartDateTime;
DateTime EndDateTime;
StartDateTime = new DateTime();
EndDateTime = new DateTime();
StartDateTime = DateTime.ParseExact(StartDate, "yyyyMMddhhmm", null);
EndDateTime = DateTime.ParseExact(EndDate, "yyyyMMddhhmm", null);
var detail = from a in db.Details where (a.callDate >= StartDateTime && a.callDate <= EndDateTime) select a;
var Response = new DetailResponse() { status = true, calls = detail };
return Ok(response);
}
However I get the error that >= can't be used in datetime and strings.
EDIT:
For the sake of one of the answer I'm including a model class I'm using to display the data.
DetailResponse.cs
public class DetailResponse
{
public bool status { get; set; }
public string statusMessage { get; set; }
public IQueryable<Detail> calls { get; set; }
}
Probably this is happening, because callDate is a string. So you can't compare a string with a datetime. The solution to this problem is to have the same type. That being said I would convert a.callDate to a DateTime.
However, I think that it would be better you change the data type of callDate in the database level. Undoubtedly, this is a personal opinion. So you don't have to follow it. Doing so your code will not need any change.
Now, in terms of code the solution I suggested above is the following:
var allDetails = db.Details.AsEnumerable();
var details = from detail in details
let callDate = DateTime.ParseExact(detail.callDate, "yyyyMMddhhmm", null)
where callDate >= StartDateTime
&& callDate <= EndDateTime
select detail;
Update
As we concluded in comments, we had to call the AsEnumerable, in order the above query to work. Why is this needed?
Borrowing Jon Skeet's words from Reimplementing Linq to Objects: Part 36 – AsEnumerable
Now it’s not entirely uncommon to want to perform some aspects of the
query in the database, and then a bit more manipulation in .NET –
particularly if there are aspects you basically can’t implement in
LINQ to SQL (or whatever provider you’re using). For example, you may
want to build a particular in-memory representation which isn’t really
amenable to the provider’s model.
The DateTime.ParseExact cannot be translated properly in a database method.
Your comparison is failing because the date in your database is of type string, try doing like this:
[ResponseType(typeof(Detail))]
public IHttpActionResult GetDetail(string StartDate, string EndDate)
{
DateTime StartDateTime = DateTime.ParseExact(StartDate, "yyyyMMddhhmm", null);
DateTime EndDateTime = DateTime.ParseExact(EndDate, "yyyyMMddhhmm", null);
var detail = from a in db.Details where (DateTime.ParseExact(a.callDate, "yyyyMMddhhmm", null) >= StartDateTime &&
DateTime.ParseExact(a.callDate, "yyyyMMddhhmm", null) <= EndDateTime) select a;
}
However you are probably best off chaning the type of your callDate to a date instead of a string.
What does your schema look like? is callDate a string? you may need to convert callDate to a DateTime before you can do the comparison.
var detail = from a in db.Details where (Convert.ToDateTime(a.callDate) >= StartDateTime && Convert.ToDateTime(a.callDate) <= EndDateTime) select a;
As has already been said, you can't compare a string to a DateTime, however, given that the date format is
yyyyMMddhhmm
(ie. year month day hour minute) where the values are all numeric and go from least varying -> most varying you will be safe doing a string comparison:
var detail = from a in db.Details where (a.callDate >= StartDate && a.callDate <= EndDate) select a;
This is because "201601010101" is less than "201612312359" when comparing strings (in the same way that "a" is less than "b").
This will save you converting the data to DateTime.
Having said that, by doing the conversion you are validating the data and can show an error if it's not in the right format.

Find TotalDays between two Datetime? variables

DateTime? arrival = (DateTime?)(t.ArrivalDate.Value);
DateTime? departure = (DateTime?)(t.DepartureDate);
Okay i know both of them are nullable and .TotalDays does not work on nullable object. So kindly tell me how am i supposed to find days difference between these two objects.
Note:
Both objects contains Date(s) i.e. are not null
Since there's no meaningful value to their difference if any of them is null, you only need to concern yourself with the case where they're not:
DateTime? arrival = (DateTime?)(t.ArrivalDate.Value);
DateTime? departure = (DateTime?)(t.DepartureDate);
double? totalDays = arrival.HasValue && departure.HasValue
? (double?)(departure - arrival).GetValueOrDefault().TotalDays
: null;
The subtraction should work because of implicit casting to DateTime.
Note: Both objects contains Date(s) i.e. are not null
If you are sure that dates never have null then you can use .Value for nullable DateTime objects. You will get exception when any of them is null.
double days = departure.Value.Subtract(arrival.Value).TotalDays;
//Set dates
DateTime? beginDate = DateTime.Now;
DateTime? endDate = DateTime.Now.AddDays(10);
//Check both values have a value (they will based on above)
//If they do get the ticks between them
long diff = 0;
if (beginDate.HasValue && endDate.HasValue)
diff = endDate.Value.Ticks - beginDate.Value.Ticks;
//Get difference in ticks as a time span to get days between.
int daysDifference = new TimeSpan(diff).Days;
Here i give you tested code please have a look :
DateTime? startDate = DateTime.Now;
DateTime? endDate = DateTime.Now.AddDays(5);
long differenceOfDays = 0;
if (startDate.HasValue && endDate.HasValue)
differenceOfDays = endDate.Value.Ticks - startDate.Value.Ticks;
int daysDifference = new TimeSpan(differenceOfDays).Days;

Linq query to get all values from between two dates

Is it possible to use linq to create a query to get a range of values from between two dates used as delimiters?
ex.
Get all entities that have a value of Date column between 12/12/12 and 12/12/13.
I'm only looking for a hint or an approach.
Of course you can, there is no "between" syntax if that's what you are getting at but it's relatively simple to do e.g.
list.Where(x => x.Date >= StartDate && x.Date <= EndDate);
If you want better readability, you could even write an extension method for DateTime i.e.
public static class DateTimeHelpers
{
public static bool Between(this DateTime dateTime, DateTime startDate, DateTime endDate)
{
return dateTime >= startDate && dateTime <= endDate;
}
}
...
list.Where(x => x.Date.Between(StartDate, EndDate));
public IEnumerable<DateTime> test(DateTime dt, DateTime dt2)
{
// check if dt is smaller (or the same) as dt2 and code this out or throw error
// replace dates with actual class / code
List<DateTime> dates = new List<DateTime>();
return dates.Where(d => d >= dt && d <= dt2);
}
var stuffBetweenDates = ListOfStuff.Where(c => c.Date >= 12/12/12 && c.Date <= 12/12/13);
Something along those lines I think. Obviously where I have put 12/12/12 and 12/12/13 you need to put recognized Date Types in those places.

DateTime difference in days on the basis of Date only

I need to find the difference in days between two dates.
For example:
Input: **startDate** = 12-31-2012 23hr:59mn:00sec, **endDate** = 01-01-2013 00hr:15mn:00sec
Expected output: 1
I tried the following:
(dt1-dt2).TotalDays and convert to integer but didn't give me appropriate answer as double has to be converted to int - tried Math.Ceiling, Convert.To...
dt1.day - dt2.day does not work across months
dt.Substract() has the same output as option 1 mentioned above.
None of the above worked, so I ended up writing the following code. The code works well, but I feel that there must be a solution with only a couple of lines of code.
public static int GetDifferenceInDaysX(this DateTime startDate, DateTime endDate)
{
//Initializing with 0 as default return value
int difference = 0;
//If either of the dates are not set then return 0 instead of throwing an exception
if (startDate == default(DateTime) | endDate == default(DateTime))
return difference;
//If the dates are same then return 0
if (startDate.ToShortDateString() == endDate.ToShortDateString())
return difference;
//startDate moving towards endDate either with increment or decrement
while (startDate.AddDays(difference).ToShortDateString() != endDate.ToShortDateString())
{
difference = (startDate < endDate) ? ++difference : --difference;
}
return difference;
}
Note: I do not have any performance issue in the while-loop iteration as the max difference will not be more than 30 to 45 days.
Well, it sounds like you want the difference in the number of days, ignoring the time component. A DateTime with the time component reset to 00:00:00 is what the Date property gives you:
(startDate.Date - endDate.Date).TotalDays
If you use the DateTime.Date property this will eliminate the time
date1.Date.Subtract(date2.Date).Days
Use TimeStamp. Just subtract two dates (using DateTime.Date property), get the difference in time span and return TotalDays
TimeSpan ts = endDate.Date - startDate.Date;
double TotalDays = ts.TotalDays;
So your extension method can be as simple as:
public static int GetDifferenceInDaysX(this DateTime startDate, DateTime endDate)
{
return (int) (endDate.Date - startDate.Date).TotalDays;
// to return just a int part of the Total days, you may round it according to your requirement
}
EDIT: Since the question has been edited, you may check the following example.
Consider the following two dates.
DateTime startDate = new DateTime(2012, 12, 31, 23, 59, 00);
DateTime endDate = new DateTime(2013, 01, 01, 00, 15, 00);
You can write the extension method as:
public static int GetDifferenceInDaysX(this DateTime startDate, DateTime endDate)
{
TimeSpan ts = endDate - startDate;
int totalDays = (int) Math.Ceiling(ts.TotalDays);
if (ts.TotalDays < 1 && ts.TotalDays > 0)
totalDays = 1;
else
totalDays = (int) (ts.TotalDays);
return totalDays;
}
For the above dates it will give you 1

Iterate over each Day between StartDate and EndDate [duplicate]

This question already has answers here:
How do I loop through a date range?
(17 answers)
Closed 6 years ago.
I have a DateTime StartDate and EndDate.
How can I, irrespective of times, iterate across each Day between those two?
Example: StartDate is 7/20/2010 5:10:32 PM and EndDate is 7/29/2010
1:59:12 AM.
I want to be able to iterate across 7/20, 7/21, 7/22 .. 7/29.
for(DateTime date = StartDate; date.Date <= EndDate.Date; date = date.AddDays(1))
{
...
}
The .Date is to make sure you have that last day, like in the example.
An alternative method that might be more reusable is to write an extension method on DateTime and return an IEnumerable.
For example, you can define a class:
public static class MyExtensions
{
public static IEnumerable EachDay(this DateTime start, DateTime end)
{
// Remove time info from start date (we only care about day).
DateTime currentDay = new DateTime(start.Year, start.Month, start.Day);
while (currentDay <= end)
{
yield return currentDay;
currentDay = currentDay.AddDays(1);
}
}
}
Now in the calling code you can do the following:
DateTime start = DateTime.Now;
DateTime end = start.AddDays(20);
foreach (var day in start.EachDay(end))
{
...
}
Another advantage to this approach is that it makes it trivial to add EachWeek, EachMonth etc. These will then all be accessible on DateTime.
You have to be careful about end-date. For example, in
Example: StartDate is 7/20/2010 5:10:32 PM and EndDate is 7/29/2010 1:59:12 AM.
I want to be able to iterate across 7/20, 7/21, 7/22 .. 7/29.
date < endDate will not include 7/29 ever. When you add 1 day to 7/28 5:10 PM - it becomes 7/29 5:10 PM which is higher than 7/29 2 AM.
If that is not what you want then I'd say you do
for (DateTime date = start.Date; date <= end.Date; date += TimeSpan.FromDays(1))
{
Console.WriteLine(date.ToString());
}
or something to that effect.
The loops of #Yuriy Faktorovich, #healsjnr and #mho will all throw a System.ArgumentOutOfRangeException: The added or subtracted value results in an un-representable DateTime
exception if EndDate == DateTime.MaxValue.
To prevent this, add an extra check at the end of the loop
for(DateTime date = StartDate; date.Date <= EndDate.Date; date = date.AddDays(1))
{
...
if (date.Date == DateTime.MaxValue.Date)
{
break;
}
}
(I would have posted this as a comment to #Yuriy Faktorovich's answer, but I lack reputation)
DateTime date = DateTime.Now;
DateTime endDate = date.AddDays(10);
while (date < endDate)
{
Console.WriteLine(date);
date = date.AddDays(1);
}

Categories