I have some dates which are currently stored as a list of strings.
For example:
List<string> dates = new List<string>();
dates.Add("1/10/14");
dates.Add("2/9/14");
dates.Add("1/15/14");
dates.Add("2/3/14");
dates.Add("2/15/14");
(The date is in mm/dd/yy format)
I will take a user's input (also in mm/dd/yy format), but as a string.
Now, I want to find the date in the array that is the next closest after the user input date.
For example, if the user enters "1/13/14", the output should be "1/15/14".
If the user enters "2/5/14", then the next closest date is "2/9/14".
But if the user enter a date that is later than the last date (say "3/1/14", it will STILL return the last date in the array which is "2/15/14")
I know at some point you have to convert to type DateTime, but I couldn't figure out the logic to find such date.
List<string> dates = new List<string>();
dates.Add("1/10/14");
dates.Add("2/9/14");
dates.Add("1/15/14");
dates.Add("2/3/14");
dates.Add("2/15/14");
var allDates = dates.Select(DateTime.Parse).OrderBy(d=>d).ToList();
var inputDate = DateTime.Parse("1/13/14");
var closestDate = inputDate >= allDates.Last()
? allDates.Last()
: inputDate <= allDates.First()
? allDates.First()
: allDates.First(d => d >= inputDate);
For now I'm just parsing strings, but you should handle it separately. This is simple plain LINQ, you can go fancy and do binary search as well.
Here is a solution that uses a Binary Search.
You need to have a list of DateTimes, no question about it. No point in keeping them as strings, parse them on the way into the list if you have to. You can use LINQ to convert all the elements in the list, but that's the only way you'll be able to compare dates with eachother.
Check out the page for BinarySearch to understand why I am using the bitwise operator on the return value.
//build list of random dates
Random r = new Random();
var dates = new List<DateTime>();
for (int i = 0; i < 10; i++)
{
dates.Add(new DateTime(2014, r.Next(1,13), r.Next(1,28)));
}
//sort list (if you don't do this, you'll most likely get the wrong answer)
dates.Sort();
//get input
string input = "1/13/14";
DateTime inputDate = DateTime.Parse(input);
//find nearest
var result = dates.BinarySearch(inputDate);
DateTime nearest;
//get match or next in list.
if(result >= 0)
nearest = dates[result];
else if (~result == dates.Count )
nearest =dates.Last();
else
nearest = dates[~result];
If you need to find the true closest, use this in place of the last if block.
//get match, or true nearest date in list
if(result >= 0) //date was found! Use that index.
nearest = dates[result];
else if (~result == 0) //date not found, smaller than any item in the list. use first index.
nearest = dates.First();
else if(~result == dates.Count) //date was not found, and is greater than all items. Use last index.
nearest = dates.Last();
else //date not found, somewhere in the middle of the list. find the nearest date
{
var daysAfter = dates[~result].Subtract(inputDate); //date after input
var daysBefore = inputDate.Subtract(dates[~result - 1]); //date before input
if(daysAfter < daysBefore)
nearest = dates[~result];
else
nearest = dates[~result - 1];
}
DateTime searchDate = new DateTime(2014,03,01);
var orderedDates = dates
.Select(d => DateTime.ParseExact(d, "M/d/yy", CultureInfo.InvariantCulture))
.OrderBy(d => d).ToList();
var result = orderedDates.FirstOrDefault(d => d > searchDate);
if (result == default(DateTime))
result = orderedDates.Last();
Something like this?
List<string> dates = YourListOfStringsHere() ; // list of strings in MM/dd/yyyy form.
DateTime desiredDate = new DateTime(2014,2,27) ;
DateTime foundDate = dates
.Select( x => DateTime.ParseExact(x,"MM/dd/yyyy",CultureInfo.CurrentCulture))
.Where( x => x >= desiredDate.Date )
.Min()
;
Why you'd be storing date/time values as a string is beyond me. When you load them, why not convert them? That way, they're easier to manipulate, you know you've got clean data. Everything becomes much simpler.
Related
I am a beginner in c # and I can not find the solution for my problem.
I am creating a personal project that allows me to send reminders, I have a date list and I need to do tasks between two specific dates in real life.
I found how to get the next date from today's date but I can't find how to get the previous one
Here is my sample code
void calc_x_date()
{
List<string> x_dates = new List<string>();
x_dates.Add("10/01/2017");
x_dates.Add("14/02/2017");
x_dates.Add("14/03/2017");
x_dates.Add("11/04/2017");
x_dates.Add("09/05/2017");
x_dates.Add("13/06/2017");
x_dates.Add("04/07/2017");
x_dates.Add("08/08/2017");
x_dates.Add("12/09/2017");
x_dates.Add("10/10/2017");
x_dates.Add("14/11/2017");
x_dates.Add("12/12/2017");
var allDates = x_dates.Select(DateTime.Parse).OrderBy(d => d).ToList();
var todayDate = DateTime.Today;
var nextDate = todayDate >= allDates.Last()
? allDates.Last()
: todayDate <= allDates.First()
? allDates.First()
: allDates.First(d => d >= todayDate);
string NextDate = nextDate.ToString(); // the closest next date from today
//string PreviousDate = // the closest previous date from today
}
Could someone explain me how to find my previous date please ?
Thanks in advance
I'd suggest using List<T>.BinarySearch: that will find the index of the date. If the index is 0 or more, then the exact date was found. If it's negative, then taking ~index will get you the index where the date would have been inserted.
Once you've got that information, it's easy to find the value at the previous index or the next index - but you should consider all the cases of:
Today is before all dates
Today is after all dates
Today is a date between the first and last date in the list, but isn't in the list
Today is equal to the first date
Today is equal to the last date
Today is equal to a date in the list which isn't the first or last date
As asides:
I would strongly encourage you to get in the habit of following .NET naming conventions as early as possible
I'd encourage you not to use strings to represent dates unless you really need to
If you're doing a lot of date/time work, you may find my Noda Time library easier to use correctly than DateTime
Here is a Linq approach
List<string> x_dates = new List<string>();
x_dates.Add("10/01/2017");
x_dates.Add("14/02/2017");
x_dates.Add("14/03/2017");
x_dates.Add("11/04/2017");
x_dates.Add("09/05/2017");
x_dates.Add("13/06/2017");
x_dates.Add("04/07/2017");
x_dates.Add("08/08/2017");
x_dates.Add("12/09/2017");
x_dates.Add("10/10/2017");
x_dates.Add("14/11/2017");
x_dates.Add("12/12/2017");
DateTime today = DateTime.Today;
IEnumerable<DateTime> dt_dates = x_dates.Select(DateTime.Parse);
DateTime prev = dt_dates.Where(x => x < today)
.OrderByDescending(x => x)
.First();
DateTime next = dt_dates.Where(x => x > today)
.OrderBy(x => x)
.First();
alternative solution
DateTime prev = dt_dates.Where(x => x < today).Max();
DateTime next = dt_dates.Where(x => x > today).Min();
Storing dates in string format works. It is however incredibly difficult to do date comparisons. You have to first cast it to numbers, handle the exceptions, etc.
C# has a DateTime object. You can store dates in this and ignore the time. DateTime objects can be compared using the < and > operators.
If you create a class with a start date and an end date, store these objects in a list(of tasks), would that solve your problem? You can also add a text of the task in a string to said object.
I have two Columns in Below format in my application (c#)
Month Name Amount
Jan15 ==2000
Feb15 ==457
Mar15 =200
April15 =4666
May15 = 357
Jan16 = 332
feb16 =323
Mar16 =233
these columns are dynamic (any number of columns can cm in Same format)
I need to get the sum of all the amounts where month is after Mar15.
How to achieve that.
Pseudo code
If MonthName >Mar15
amount = sum (amount)
If your DateFormat is always be going to like Jan-14 Feb-14 Mar-14 Apr-14 May-14 Jun-14 then you can try to use the ParseExact to convert it to the DateTime and then apply the SUM on it to get the result you want.
var result = dataList.Where(x => DateTime.ParseExact(x.monthName, "MMM-yy", CultureInfo.InvariantCulture) > DateTime.ParseExact("Mar-15", "MMM-yy", CultureInfo.InvariantCulture))
.Sum(x => x.Amount);
In above case it will give the sum of all amounts which has date greater than Mar-15.
UPDATE
To add the Group BY you can simply add as,
.GroupBy(x=>x.Id).Select(x=> new { Person = x.Key,TotalAmount = x.Sum(x=>x.Amount)})
A part of my project is to write a function that will generate an increasing number for every day for every record I have to store in a database table. My problem is that when I try to compare the DateTime.Now with the column I have in my database, I try to compare Datetime with Date so I never have equality on the days. My code explains better than me my issue:
var r = (from i in context.vehicles
where i.InsertionDate == DateTime.Now
select i); // In this query i cant compare the two dates. The one is datetime and the other is date format
int result3 = 0;
if (r.Any())
{
var result = r.OrderByDescending(ii => ii.IncreasingNumberOfTheDay).FirstOrDefault();
int myint = Convert.ToInt32(result.IncreasingNumberOfTheDay);
result3 = myint + 1;
}
else
result3 = 1;
I suspect you just want:
DateTime today = DateTime.Today; // Just fetch the date part.
var query = context.vehicles.Where(v => v.InsertionDate == today);
Avoid string conversions if you possibly can.
If your InsertionDate includes times, then either you need to try to get the SQL query to truncate that to a date, or you could use:
DateTime today = DateTime.Today;
DateTime tomorrow = today.AddDays(1);
var query = context.vehicles.Where(v => v.InsertionDate >= today &&
v.InsertionDate < tomorrow);
What I need is to get logic on how to get monthname-year between two dates.
Dictionary<Monthname,year> GetMonthsandYear(Datetime d1,Datetime d2)
or
List<Tuple<string,int> GetMonthsandYear(Datetime d1,Datetime d2)
example : jan-1-2013 to mar-3-2013
should return January-2013,february-2013,march-2013 or in reverse format by list.reverse
If your actual requirement is "the previous 24 months" then it's much simpler. Just start off with the current month, and get the 1st day of it - then iterate and add 1 month 24 times.
Personally I'd return an IEnumerable<DateTime> rather than anything else - you can format each element however you want - but it's pretty simple:
public static IEnumerable<DateTime> GetMonths(int count)
{
// Note: this uses the system local time zone. Are you sure that's what
// you want?
var today = DateTime.Today;
// Always return the 1st of the month, so we don't need to worry about
// what "March 30th - 1 month" means
var startOfMonth = new DateTime(today.Year, today.Month, 1);
for (int i = 0; i < count; i++)
{
yield return startOfMonth;
startOfMonth = startOfMonth.AddMonths(-1);
}
}
Then if you want a List<string> of these values as "February 2014" etc for example, you could have:
var monthYears = GetMonths(24).Select(dt => dt.ToString("MMMM yyyy"))
.ToList();
Note that a Dictionary<...> would not be appropriate unless you really don't care about the order - and I suspect you do. You shouldn't rely on the order in which items are returned from a Dictionary<TKey, TValue> when you view it as a sequence - it's not intended to be an ordered collection.
I don't understand why you need Dictionary or List<Tuple<string,int> but one solution could be;
DateTime dt1 = new DateTime(2013, 1, 1);
DateTime dt2 = new DateTime(2013, 3, 3);
while (dt1 < dt2)
{
Console.WriteLine(dt1.ToString("MMMM-yyyy"));
dt1 = dt1.AddMonths(1);
}
Result will be;
January-2013
February-2013
March-2013
Even if you need, you can add these values to a List<string> in while loop.
But be carefull about what Jon said, this solution will generate only January and February if your dt1.Day is greater than dt2.Day.
Suppose I have an array of dates
DateTime[] dt = new DateTime[10];
// populate array with dates
How can I find the maximum date without using LINQ?
var maxDate = DateTime.MinValue;
foreach (var date in dt) {
maxDate = date > maxDate ? date : maxDate;
}
You mention using a sorting algorithm, presumably so that you can just ask for:
var maxDate = dt[dt.Length - 1];
After the array was sorted. The thing is, you'd need to find a sorting algorithm that performs better than O(n) to gain any advantage. Bubble Sort is O(n^2) so there's no point in using it; it performs worse, on average, than just running through the list once.
In the code below i initialise the max date to an 'empty' value, then i loop through the array and update the max date if we don't have one yet, or the one we do have is before the one in the array.
DateTime? Max = null;
foreach(var d in dt)
{
if(Max == null || Max.Value < d)
Max = d;
}
The above deals with an empty array and an actual date of DateTime.MinValue
DateTime maxDate = DateTime.MinValue;
foreach (DateTime date in dt
{
if (date > maxDate)
{
maxDate = date;
}
}
Here's a solution which uses sorting (modifies the array so that it becomes sorted):
Array.Sort(dt);
DateTime maxValue = dt[dt.Length - 1];