Linq Search Date - c#

how do I used date on searching using Linq. I think I'm missing something on the declaration
string searchName = Request.Form["PersonName"];
DateTime searchDateFrom = Request.Form["ReceivedDateFrom"];
DateTime searchDateTo = Request.Form["ReceivedDateTo"];
var Results = (from va in _db.myTable
where ((va.PersonName.Contains(searchName)
&& (va.ApplicationReceivedDate > searchDateFrom
&& va.ApplicationReceivedDate < searchDateTo)
select va).ToList();

HttpRequest.Form is a NameValueCollection, where you can get strings in it by int/string indexer. That is, Request.Form["ReceivedDateFrom"] returns a string, you can't assign it to a variable whose type is DateTime without any convert. You can try DateTime.ParseExact method to convert the string to a DateTime. But if you can't guarantee the string has a correct format, you can use a TryParse method.

Might be a typo, but you need to cast searchDateFrom / searchDateTo to a DateTime and you have two extra open brackets in your linq statement

I'd also recommand using a cleaner indentation, it's easier to follow and count the brackets and stuff.
string searchName = Request.Form["PersonName"];
DateTime searchDateFrom = Request.Form["ReceivedDateFrom"];
DateTime searchDateTo = Request.Form["ReceivedDateTo"];
var Results = (from va in _db.myTable
where va.PersonName.Contains(searchName)
&& (va.ApplicationReceivedDate > searchDateFrom
&& va.ApplicationReceivedDate < searchDateTo)
select va).ToList();

This is when I setup a unit test to see what's going on.
Check your brackets and casting from string to datetime:
[TestMethod]
public void TestMethod1()
{
List<myTable> myTables = new List<myTable>();
for (int month = 1; month < 10; month++)
{
for (int day = 1; day < 20; day++)
{
myTables.Add(new myTable { PersonName = "Person " + month.ToString() + " " + day.ToString(), ApplicationReceivedDate = new DateTime(2011, month, day) });
}
}
string searchName = "Person";
DateTime searchDateFrom = Convert.ToDateTime("2011-01-02");
DateTime searchDateTo = Convert.ToDateTime("2011-01-03");
var Results = (from va in myTables
where va.PersonName.Contains(searchName)
&& va.ApplicationReceivedDate >= searchDateFrom
&& va.ApplicationReceivedDate < searchDateTo
select va);
Assert.AreEqual(Results.Count(), 1);
}
public class myTable
{
public string PersonName { get; set; }
public DateTime ApplicationReceivedDate { get; set; }
}
Also check the search from & to.

Related

Determining the date logic based on a given string in c#

I have written a logic in c# that determines the nextCallDate based on the given cobDate. cobDate is current date -1.
So if there are more than one future date in the given string then it should return the nearest future date to the cobdate and ignore the rest
For eg if the cobdate is 2020/02/12 and the string is
;2;4;2;5;20180328;3;103.3750;5;20190328;3;102.250;5; 20200328;3;101.1250;5;20210328;3;100.00;
Then NextCallDate would be 2020/03/28.
I need to return blank for dates in the past.
So say in the example if the given string has all the dates in the past then it should return blank.
Given string ;2;1;2;5;20120918;3;100.000000;
Here is what I have written
private DateTime? GetNextCallDate(string nextCallDate)
{
DateTime cobDate = DateTime.Now.Date.AddDays(-1);
var parts = nextCallDate.Split(';');
foreach (var part in parts)
{
DateTime parsedNextCallDate = DateTime.Parse(part);
if (parsedNextCallDate.Date > cobDate.Date)
{
return parsedNextCallDate;
}
}
return null;
}
You should probably be using DateTime.TryParse instead of Parse, since some of the values are not dates. Also, it looks like you're returning the first date that's greater than cobDate, not the nearest one.
To resolve this, we first set parsedNextCallDate to null, and this will be our default return value. Then we can check each part if it's a DateTime using the return value from TryParse, and then compare the value to both cobDate and parsedNextCallDate. If the date is greater than cobDate and less than parasedNextCallDate (or if parasedNextCallDate isn't set yet), then we update parasedNextCallDate to the new value. At the end, we just return parasedNextCallDate:
public static DateTime? GetNextCallDate(string input)
{
DateTime? nextCallDate = null;
if (string.IsNullOrWhiteSpace(input)) return nextCallDate;
var yesterday = DateTime.Today.AddDays(-1);
var inputItems = input.Split(';');
foreach (var inputItem in inputItems)
{
DateTime itemDate;
// If inputItem is a DateTime and it's greater than yesterday
if (DateTime.TryParseExact(inputItem.Trim(), "yyyyMMdd", null,
DateTimeStyles.None, out itemDate) &&
itemDate.Date > yesterday)
{
// and if nextCallDate doesn't have a value or the parsed value
// is less than nextCallDate, assign nextCallDate to this value
if (!nextCallDate.HasValue || itemDate < nextCallDate)
{
nextCallDate = itemDate;
}
}
}
return nextCallDate;
}
Here's one way solve your problem. Breaking things down into steps often makes things easier to reason with and easier to test. I'm often working on server side apps so I like the new span/memory classes. So first thing is to split our input string into chunks:
static IEnumerable<ReadOnlyMemory<char>> ReduceToPossibleDates(string source)
{
const int ExpectedDateLen = 9; // includes separator
int last = 0;
var mem = source.AsMemory();
for (int i = 0; i < source.Length; ++i)
{
if (';' == mem.Span[i])
{
int length = i - last;
if (length == ExpectedDateLen)
{
yield return mem.Slice(last+1,length-1);
}
last = i;
}
}
}
This gives us a stream of ReadOnlyMemory that all contains what we think should be dates. Next we can do another method to consume those chunks and turn them into dates.
static IEnumerable<DateTime> ToDateTime(IEnumerable<ReadOnlyMemory<char>> rawDates)
{
foreach (var rawDate in rawDates)
{
if (DateTime.TryParseExact(rawDate.Span,"yyyyMMdd".AsSpan(),
CultureInfo.InvariantCulture,
DateTimeStyles.None, out var date))
yield return date;
}
}
Once we have that we can treat the stream of dates however we want. In this case we check to find the first that's after our COB.
static void Main(string[] _)
{
const string GoodData = ";2;4;2;5;20180328;3;103.3750;5;20190328;3;102.250;"+
"5;20200328;3;101.1250;5;20210328;3;100.00;";
const string NoDateData = ";2;1;2;5;20120918;3;100.000000;";
var cobDate = new DateTime(2020, 2,12); // some actual close of business date...
var nextCallDate = ToDateTime(ReduceToPossibleDates(GoodData))
.FirstOrDefault(x => x >= cobDate);
var noDateExpected = ToDateTime(ReduceToPossibleDates(NoDateData))
.FirstOrDefault(x => x >= cobDate);
if (nextCallDate != default(DateTime))
Console.WriteLine(nextCallDate);
else
Console.WriteLine("no call date.");
if (noDateExpected != default(DateTime))
Console.WriteLine(nextCallDate);
else
Console.WriteLine("no call date.");
}
It would be a little cleaner with extension methods but you get the idea.

How to output DateTime array elements where DateTime is greater than

I have two arrays, UserActivity and UserDateTime. User Activity array holds the activities of what the user is doing and the UserDateTime holds the DateTime of the said activity. I cannot find a solution where I can output the DateTime where it's greater than such Date/Time whilst printing out the user activity.
For example:
[UserDateTime] [UserActivity]
02/02/2018 02:20 User logs on
05/02/2018 15:20 User visits page
20/02/2018 16:10 User goes here
21/02/2018 12:00 User logs off
21/02/2018 13:00 User logs on
21/02/2018 15:00 User visits here
The Date and time has its own array (UserDateTime) while the users' activity is in the array UserActivity'.
etc.
My problem is that I can't programmatically output where the DateTime is greater than 20/02/2019 and the User Activity is linked to the date/time.
My code is followed:
string[] UserActivity = File.ReadAllLines(#"useractivityfile");
string[] UserDateTime = File.ReadAllLines(#"userdatetimesfile");
DateTime greaterthanthis = new DateTime(2018, 2, 20);
for (int i = 0; i < UserActivity.Length; i++)
{
if (DateTime.ParseExact(UserDT[i], "dd/MM/yyyy", CultureInfo.InvariantCulture) > greaterthanthis)
{
Console.WriteLine(UserDT[i].Where(x >= greaterthanthis????); //very stuck on this part
//Also want to link the useractivity so Console.WriteLine(UserDT[i] + UserDT[i])
}
}
I've been overthinking and overcomplicating this code all week (stressed). Thank you for your help.
Try this pls. The format of the string representation must match a specified format exactly or an exception is thrown.
string[] UserActivity = File.ReadAllLines(#"path");
string[] UserDateTime = File.ReadAllLines(#"path");
DateTime greaterthanthis = new DateTime(2018, 2, 20);
for (int i = 0; i < UserActivity.Length; i++)
{
if (DateTime.ParseExact(UserDateTime[i], "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture) > greaterthanthis)
{
Console.WriteLine(UserDateTime[i].ToString() + " : " + UserActivity[i]);
}
}
There is plenty of typo in your code:
for (int i = 0; x < UserActivity.Length; i++)
=> for (int i = 0; i < UserActivity.Length; i++)
if (DateTime.ParseExact(UserDT[i], "dd/MM/yyyy", CultureInfo.InvariantCulture > greaterthanthis ) )
=> if (DateTime.ParseExact(UserDT[i], "dd/MM/yyyy", CultureInfo.InvariantCulture ) > greaterthanthis)
The ParseExact format match your 1rst version but doesn't match your file format
The easier step will be to get away from those files and make a reasonable object out of it:
public class Logs
{
public DateTime Date { get; set; }
public string Name { get; set; }
public string Activity { get; set; }
}
Note that you example bothe file don't have the same number of lines...
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
public class Logs
{
public DateTime Date { get; set; }
public string Name { get; set; }
public string Activity { get; set; }
}
public static void Main(string[] args)
{
var UserDateTime_TEXT = #"02/02/2018 02:20
05/02/2018 15:20
21/02/2018 12:00
21/02/2018 13:00
21/02/2018 15:00 ";
var UserActivity_TEXT = #"User logs on
User visits page
User goes here
User logs off
User logs on
User visits here";
//Equivalent to ReadAllLines
string[] UserDateTime = UserDateTime_TEXT.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
string[] UserActivity = UserActivity_TEXT.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
// ps in your exemple there is more activity than date time...
// UserDateTime.Length != UserActivity.Length !!!!!!!!!!!
var data = new List<Logs>();
//in your code you had `x < UserActivity.Length`, what is x?
for (int i = 0; i < UserDateTime.Length; i++)
{
var splitValues = UserActivity[i].Split(' ');
var temp =
new Logs
{
Date = DateTime.ParseExact(UserDateTime[i].Trim(), "dd/MM/yyyy HH:mm", CultureInfo.InvariantCulture),
Name = splitValues[0], // No space in username.
Activity = string.Join(" ", splitValues.Skip(1))
};
data.Add(temp);
}
//filter
var greaterThanThis = new DateTime(2018, 2, 20);
var result = data.Where(x=> x.Date > greaterThanThis);
foreach(var entry in data){
Console.WriteLine($"{entry.Name} did {entry.Activity} at {entry.Date}");
}
}
Live Demo
Frankly speaking, your code looks like a complete mess. Brackets were wrong, variable names were wrong, LINQ query was wrong as well.
Use this as a start:
string[] UserActivity = File.ReadAllLines(#"useractivityfile");
string[] UserDateTime = File.ReadAllLines(#"userdatetimesfile");
DateTime greaterthanthis = new DateTime(2019, 1, 12);
for (int i = 0; i < UserActivity.Length; i++)
{
if (DateTime.ParseExact(UserDateTime[i], "dd/MM/yyyy", CultureInfo.InvariantCulture) > greaterthanthis)
{
Console.WriteLine(UserDateTime[i]);
//Also want to link the useractivity so Console.WriteLine(UserDT[i] + UserDT[i])
}
}
If you want a LINQ query, try this:
UserDateTime.Where(x => DateTime.ParseExact(x, "dd/MM/yyyy", CultureInfo.InvariantCulture) > greaterthanthis).ToList().ForEach(dt => Console.WriteLine(dt));
But I recommend to stay with the upper code for an easier overview and debugging.

How To Display Date In Locale For 0 Values?

Ok, so I have a date stored in UK format (dd/mm/yy) which I need to display in the locale of wherever the user might be.
The issue is that this date can be 000000 (00/00/2000); so I can't convert it to DateTime directly, as DateTime doesn't support 0 values for day or month.
I have this so far:
int dateInt = ddmmyy;
var year = (dateInt % 100) + 2000;
var month = (dateInt / 100) % 100;
var day = (dateInt / 100000);
var result = new DateTime(year, month, day); //2014/00/00 at this point, so breaks.
var resultStr = result.ToString(CultureInfo.InvariantCulture);
return resultStr;
What's the correct way to add support for 0 values initially? I've tried changing the 0 to 1 before converting to DateTime, running the conversion and then replacing with a 0 again; but due to culture variants I see no way that this method can support other cultures, which is the purpose of this conversion to begin with.
Any ideas? I'm guessing this is a common issue.
Is this what you need ?
using System;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
int[] savedDates = new int[] { 000000, 010000, 000013 };
foreach (var item in savedDates)
{
DateTime date = ConvertToDate(item);
Console.WriteLine(item.ToString("D6") + " => " + date.ToShortDateString());
}
Console.ReadLine();
}
private static DateTime ConvertToDate(int item)
{
string temp = item.ToString("D6");
int day = int.Parse(temp.Substring(0, 2));
int month = int.Parse(temp.Substring(2, 2));
int year = int.Parse(temp.Substring(4, 2));
if (day == 0)
day = 1;
if (month == 0)
month = 1;
year += 2000;
return new DateTime(year, month, day);
}
}
}
I would not store dates like this as the methodology for doing so is already provided by the .NET framework.
The best way to store dates would be to use Culture.InvariantCulture for string conversion cases and then convert to local culture for display purposes as necessary. DateTime itself is culture-independent so converting between cultures is very easy.

How to select a date interval in IQueryable<>?

I have a IQueryable<Journey> that i collect from my entity model. I want to use this to get a new set of IQueryable<Journey> but only within a specific date interval from 2 textboxes on my webpage.
A Journey has Journey.DateFrom and Journey.DateTo which are strings ("YYYYMMDD").
I thought i would do something like this:
(journeys is IQueryable<Journey>)
if (tb_DateFrom.Text != ""){
journeys = from j in journeys
where Convert.ToInt32(j.DateTo) >= Convert.ToInt32(tb_DateFrom.Text)
select j;
}
if (tb_DateTo.Text != ""){
journeys = from j in journeys
where Convert.ToInt32(j.DateFrom) <= Convert.ToInt32(tb_DateTo.Text)
select j;
}
But i get error saying that linq doesnt know how to do Convert.ToInt32, neither does it know how to do int.parse or datetime.parse. What works is to use IEnumerable<Journey> instead of IQueryable<Journey> but that is so slow that the website crash since the data im comparing is quite huge.
How can i work this out, is the only answer to get the format in db to datetime?
Please help :)
I'd try this:
if (tb_DateFrom.Text != "") {
journeys = from j in journeys
where j.DateTo.CompareTo(tb_DateFrom.Text) >= 0
select j;
}
if (tb_DateTo.Text != "") {
journeys = from j in journeys
where j.DateFrom.CompareTo(tb_DateTo.Text) <= 0
select j;
}
Why don't you convert textbox values to datetime and then compare the dates in the where clause, instead of converting to int
DateTime? dateFrom = null, dateTo = null;
if(!String.IsNullOrWhiteSpace(tb_DateFrom.Text))
dateFrom = DateTime.ParseExact(tb_DateFrom.Text, "yyyyMMdd", null);
if (!String.IsNullOrWhiteSpace(tb_DateTo.Text))
dateTo = DateTime.ParseExact(tb_DateTo.Text, "yyyyMMdd", null);
if (dateFrom.HasValue)
journeys = journeys.Where(j => j.DateFrom >= dateFrom.Value);
if (dateTo.HasValue)
journeys = journeys.Where(j => j.DateTo <= dateTo.Value);
private DateTime getDate(string yyyyMmDd, DateTime defaultValue)
{
DateTime ret = DateTime.MinValue;
if (!DateTime.TryParse(yyyyMmDd, out ret))
return defaultValue;
return ret;
}
var to = DateTime.Parse(tb_DateTo.Text);
var from = DateTime.Parse(tb_DateFrom.Text);
journeys.Where(j=> getDate(j.DateFrom, DateTime.MaxValue) <= from && getDate(j.DateTo, DateTime.MinValue) >= to);
As the string format you have sorts in the same order as the dates they represent, I don't see why you have to convert their data format at all. Just do (untested):
journeys = from j in journeys
where j.DateTo >= tb_DateFrom.Text && j.DateFrom >= tb_DateTo.Text
select j;
Update, after Joakim's comment, still just using the sort order of the strings:
journeys = from j in journeys
where j.DateTo.CompareTo(tb_DateFrom.Text) >= 0 &&
j.DateFrom.CompareTo(tb_DateTo.Text) <= 0
select j;
(Det borde väl fungera, Joakim?)
Oops, I missed the accepted answer, but I'll still leave my first edit...

populate drop down list with month/year

I have a date and I need to populate a drop-down with the months/years between that date and today. For instance, if that date is 10/14/2010 then the drop-down should contain October 2010, November 2010, December 2010, January 2011.
The way I'm thinking of doing this is to pass that date to a function, loop from today backwards step 1 month while adding each month to a collection until we reach that date and finally return a collection of strings. Then, populate the drop-down control on page load. Finally, use some ajax with a page method to parse back the string and trigger a partial page reload.
I'm just wondering if there's an easy way to do it.
Thanks.
Maybe you can try this:
static IEnumerable<DateTime> monthsBetween(DateTime startDate, DateTime endDate)
{
return Enumerable.Range(0, (endDate.Year - startDate.Year) * 12 + (endDate.Month - startDate.Month + 1))
.Select(m => new DateTime(startDate.Year, startDate.Month, 1).AddMonths(m));
}
This will not give you the result in the exact format that you want, but you get the drift. :)
You can do something like this which is pretty much what you described except counting forward:
private string[] FillDropDownWithDates(DateTime dt)
{
DateTime dtnow = DateTime.Now;
List<string> values = new List<string>();
if ( (dt <= dtnow))
{
values.Add(String.Format("{0:y}", dt));
}
while ( (dt = dt.AddMonths(1)) <= dtnow || ( dt.Month == dtnow.Month && dt.Year == dtnow.Year) )
{
values.Add(String.Format("{0:y}", dt)); // "March, 2008" YearMonth
}
return values.ToArray();
}
public static List<string> GetMonths(DateTime StartDate)
{
List<string> MonthList = new List<string>();
DateTime ThisMonth = DateTime.Now.Date;
while (ThisMonth.Date > StartDate.Date)
{
MonthList.Add(ThisMonth.ToString("MMMM") + " " + ThisMonth.Year.ToString());
ThisMonth = ThisMonth.AddMonths(-1);
}
return MonthList;
}
For Year,
public static IEnumerable<int> Range (int start, int count)
{
int end = start + count;
for (int i = start; i < end; i++)
yield return i;
}
var startYear = 2000;
YearDropDownList.ItemsSource= Enumerable.Range(startYear, 2050 - startYear + 1);
For Month,
An enumerable list with .ToString("MMMM") format.
This is how I got a 12 month/year. Hope the code helps.
public IEnumerable<SelectListItem> Additional12Months {
get
{
return Enumerable.Range(12, 12).Select(i => new SelectListItem { Value = DateTime.Now.AddMonths(-(i)).ToShortDateString(), Text = DateTime.Now.AddMonths(-(i)).ToString("MMM-yyyy") }).ToList();
}
}

Categories

Resources