It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
ok so this should be simple really but seems I can't get my head around it.
I have a table called Dates which has a listing of all dates starting 2012 until its very end. Now when a customer makes reservation, I need to take starting date and ending date, generate the range between them in datetime format, and check each against its equivalent date in table to see capacity. if so, then decrement the capacity by booking requirement.
so far I have this code:
private List<DateTime> getDateRange(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
{
return null;
}
List<DateTime> rv = new List<DateTime>();
DateTime tmpDate = startDate;
do
{
rv.Add(tmpDate);
tmpDate = tmpDate.AddDays(1);
} while (tmpDate <= endDate);
return rv;
}
The code is supposed to generate a list with dates in range and is called later in this function:
public void checkDateRange(DateTime startDate, DateTime endDate)
{
//need to check each element in the list against the table DateTest:
//changing the parameters of parse will give error. Require type handling
//First, extract the elements:
DateTime StartingDate = DateTime.Parse("02/25/2007");
DateTime EndingDate = DateTime.Parse("03/06/2007");
foreach (DateTime date in getDateRange(StartingDate, EndingDate))
{
date.ToShortDateString();
}
//rest of code should go here...
}
am thinking more like retrieve whole set from table DateTest, then in a loop (not sure which) I would go about checking if x = y these are the dates. Any code illustration would be highly appreciated. Regards,
Based on a (total) guess of what you're trying to do here:
public class Program
{
static void Main(string[] args)
{
TestDateRange(DateTime.Today, DateTime.Today.AddMonths(1));
}
Dictionary<DateTime, List<string>> bookings;
int maxOccupancy = 2;
public Program(int year = 2011)
{
bookings = getDateRange(new DateTime(year, 1, 1), new DateTime(year,12,31)).ToDictionary(day => day, day => new List<string>());
}
private static void TestDateRange(DateTime startDate, DateTime endDate)
{
Program p = new Program();
if (p.GetFullDaysInDateRange(startDate, endDate).Count() == 0)
{
string bookingName = "Booking for test";
p.AddBooking(startDate, endDate, bookingName);
}
}
private IEnumerable<DateTime> getDateRange(DateTime startDate, DateTime endDate)
{
if (startDate > endDate)
{
return null;
}
return Enumerable.Range((int)startDate.ToOADate(), endDate.Subtract(startDate).Days).Select(day => DateTime.FromOADate(day));
}
private void AddBooking(DateTime startDate, DateTime endDate, string name)
{
IEnumerable<DateTime> range = getDateRange(startDate, endDate);
foreach (DateTime date in range)
{
if (bookings[date].Contains(name))
return; //already placed this booking
if (bookings[date].Count > maxOccupancy)
throw new Exception(String.Format("Cannot book on {0}: full", date));
bookings[date].Add(name);
}
}
public IEnumerable<DateTime> GetFullDaysInDateRange(DateTime startDate, DateTime endDate)
{
IEnumerable<DateTime> testRange = getDateRange(startDate, endDate);
List<DateTime> bookedDays = new List<DateTime>();
foreach (DateTime date in testRange)
{
if (bookings[date].Count > maxOccupancy)
bookedDays.Add(date);
}
return bookedDays;
}
}
I haven't looked at your code, but here are a few comments:
The range between a start date and end date is a TimeSpan, not a DateTime
If you are interested in reconstructing either the StartDate or EndDate, you can add or subtract the TimeSpan with the other value
If you are interested in checking for overlap, then you have a one-dimensional intersection test
Here is how to do a 1-dimensional intersection test:
One-Dimensional Line-Segments/Ranges Intersection Test: Solution Name?
bool intersects = !(
(activity1.End < activity2.Begin)
|| (activity2.End < activity1.Begin)
);
Once you have validated that there is an overlap between two date ranges, here is a way to calculate that overlap:
DateTime end = activity1.End < activity2.End
? activity1.End
: activity2.End;
DateTime beginning = activity1.Begin > activity2.Begin
? activity1.Begin
: activity2.Begin;
With these checks in hand, I have a potential solution for you.
Potential Solution
You can make a special data structure that doesn't contain individual appointments, but instead contains all consumed capacity.
When you add a reservation to this data structure, you check if the new value overlaps existing reservations. You calculate its intersection between all existing values. For each value that it overlaps, add just the overlapped portion to a list, including the currently booked capacity at that overlap.
Once all overlaps have been calculated, check if any of the items in the list have reached max capacity. If they have, then the booking is invalid, because it will make you over capacity. If they haven't you can add the reservation.
To add the reservation, increment all the intersection values' booking counts by one. Then remove all existing values in the data structure on the range of the reservation. Split items if necessary to remove only the overlapped portion. When you are done, add the incremented intersections back into the list.
You can do a similar but reversed operation when removing a booking, but you'll have to merge bookings back together at the end/beginning of the range instead of splitting them apart.
Or you can model this the opposite way, subtracting available capacity. Whichever makes more sense to you.
This model will work well with modelling variable capacity because you can model your capacity with a similar data structure. You'd just do additional intersection tests between consumed capacity and available capacity, ensuring you always have enough available capacity to satisfy the booked capacity.
I would be careful to design a system where this wasn't the only data I relied on, and I could regenerate this data from data in a separate structure/table. For example, I would store the start and end dates and contact information for each booking separately.
Optimization
You can take advantage of sorting and space partitioning algorithms to speed up the part where you compare against all booked values. For example, you could partition out all the values into month-long chunks, so you only have to compare against the one or two months that your booking intersects. In relational database land this is called temporal partitioning.
If you choose to do partitioning, one requirement that can make your life simpler is to make sure your partitions are large enough that you will only ever need to check two cells. Make your partitions smaller than this, and your algorithm will be more complicated and therefore harder to verify.
If your data is small enough, you could calculate this whole table on the fly for just the date range you are interested in, rather than storing the data structure in the database. You might also not have to worry about partitioning if your ranges are small enough that you can stand to check every day (or whatever unit, if you have a different resolution) within the range. This would allow you to simplify some of the operations because you can just build up your data structure and do your capacity check, and you won't have to worry about things like splitting up or merging capacity ranges.
Related
I am getting the data from a stock provider that includes the data for Extended Trading Hours.
The data is aggregated 1-minute data points that I convert from JSON to List<Aggregate> where aggregate contains High, Low, Open, Close, and Time of the start of the aggregated 1-min window.
My queries encompass multiple trading days - could be hundreds and could be thousands. And my calculation of indicators must exclude after-hours data.
So I have a list of hundreds of items of the object:
public class Aggregate
{
public decimal Close { get; set; }
public decimal UnixTimestamp { get; set; } // milliseconds
...
}
and also the DateTime FromDate and DateTime ToDate which spans hundreds of days.
Now, one sure way to filter the after-hours data is to compare the UnixTimestamp of each item to the regular trading time window 9:30 AM PST - 16:00 PM PST. That would require converting every UnixTimestamp to DateTime. This is a lot of comparison... Intuitively, it is better converting FromDate and ToDate to UnixTimestamp and filtering items out with Linq.
long unixTimeMsecToDate = new DateTimeOffset(aggrParams.ToDate).ToUnixTimeMilliseconds();
long unixTimeMsecFromDate = new DateTimeOffset(aggrParams.FromDate).ToUnixTimeMilliseconds();
However, I can't put my finger on exact code... Looping through the dates? How?
I managed to get rid of Aggregate object and replace it by Quote object that come from a different library with time expressed as C# Date Time and resolve the problem in a straight way as follows, effectively filtering out After-hours data points:
private void FilterExtendedTradingHours(List<Quote> quoteList, AggrParams aggrParams)
{
using (Log.VerboseCall())
{
if (aggrParams.Interval == "minute" || aggrParams.Interval == "hour")
{
Log.VerboseFormat("Initial {0} count: {1}", aggrParams.Interval, quoteList.Count);
for(int i = quoteList.Count - 1; i >= 0; i--)
{
if ( quoteList[i].Date.TimeOfDay > new TimeSpan(16, 0, 0) || quoteList[i].Date.TimeOfDay < new TimeSpan(9, 30, 0) )
{
quoteList.RemoveAt(i);
}
}
Log.VerboseFormat("Post-filtering {0} count: {1}", aggrParams.Interval, quoteList.Count);
}
}
}
Is there a faster way of obtaining the closest previous (past) DateTime from a list of DateTimes when compared to a specific time? (the list comes from a SQL database)
public DateTime GetClosestPreviousDateTime(List<DateTime> dateTimes, DateTime specificTime)
{
DateTime ret = null;
var lowestDifference = TimeSpan.MaxValue;
foreach (var date in dateTimes)
{
if (date >= specificTime)
continue;
var difference = specificTime- date;
if (difference < lowestDifference)
{
lowestDifference = difference;
ret = date;
}
}
return ret;
}
The source list will be sorted since the dates in the list come from a SQL database where they are written consecutively.
It depends what you mean by "faster". The algorithm you show is O(N) so no you won't get faster than that - if by faster you mean is there a way to not have to iterate over all dates.
But if you mean can you shave off a few microseconds with some code that doesn't emit quite as many op codes, then yes of course. But is that really the issue here?
The answer will also change based on the size of the list, how accurate you need the answer to be, whether we can make any assumptions on the data (e.g. is it already sorted).
dateTimes.Sort();
var closest = dateTimes[dateTimes.IndexOf(search) - 1];
Your problem is a classic search algorithm and binary search might suit you.
Sort list: dateTimes.Sort();
Apply Binary Search algo with similar logic as in your for statement.
dateTimes.Where(x => x < specificTime).Max()
or if you want to handle the case where none exist:
dateTimes.Where(x => x < specificTime).DefaultIfEmpty().Max()
Later edit: Now you introduce new information that the List<> is already sorted. That was not in the question before.
With a sorted List<>, your algorithm is silly since it foreaches on and on, even after having reached the point where the entries "pass" the threshold specificTime. You can use instead BinarySearch (assuming List<> is sorted in ascending order and contains no duplicates):
static DateTime GetClosestPreviousDateTime(List<DateTime> dateTimes, DateTime specificTime)
{
var search = dateTimes.BinarySearch(specificTime);
var index = (search < 0 ? ~search : search) - 1;
if (index == -1)
throw new InvalidOperationException("Not found");
return dateTimes[index];
}
If you want to do it faster, just ask the database for the value, it will know how to find the answer fast; do not fetch the entire List<> to memory first. Use SQL or LINQ to the database.
I have a function which calculates employee hours in a day. But, it's slow because it considers carry hours and other things.
I figure I can optimize this by caching the hours in a day and only update on change.
I'm looking for something that can quickly do:
Set(date,hours)
HasHoursFor(date)
GetHoursFor(date)
What should I use for this?
I could use a Dictionary, but I am wondering if there is not some sort of hash way to set, get, and contains quickly.
You could use a Dictionary<DateTime,int> and cache the hours against the date, like so:
Dictionary<DateTime,int> dict = new Dictionary<DateTime,int>();
void Set(DateTime date, int hours)
{
if (dict.Contains(date)) dict.Remove(date);
dict.Add(date.Date,hours);
}
bool HasHoursForDate(DateTime date)
{
return dict.Contains(date.Date);
}
int GetHoursForDate(DateTime date)
{
return dict[date.Date];
}
Set(DateTime.Now,8);
I normalise the date so that's its JUST the date and doesn't care about the time part, otherwise that would throw off the comparison. I'm also assuming you have whole hours otherwise you might want to change int to double etc.
I have a list of dates that a machine has worked on, but it doesn't include a date that machine was down. I need to create a list of days worked and not worked. I am not sure of the best way to do this. I have started by incrementing through all the days of a range and checking to see if the date is in the list by iterating through the entire list each time. I am looking for a more efficient means of finding the dates.
class machineday
{
datetime WorkingDay;
}
class machinedaycollection : List<machineday>
{
public List<TimeCatEvent> GetAllByCat(string cat)
{
_CategoryCode = cat;
List<machineday> li = this.FindAll(delegate(machinedaydummy) { return true; });
li.Sort(sortDate);
return li;
}
int sortDate(machinedayevent1, machinedayevent2)
{
int returnValue = -1;
if (event2.date < event1.date)
{
returnValue = 0;
}
else if (event2.date == event1.date)
{
//descending
returnValue = event1.date.CompareTo(event2.date);
}
return returnValue;
}
}
Sort the dates and iterate the resulting list in parallel to incrementing a counter. Whenever the counter does not match the current list element, you've found a date missing in the list.
List<DateTime> days = ...;
days.Sort();
DateTime dt = days[0].Date;
for (int i = 0; i < days.Length; dt = dt.AddDays(1))
{
if (dt == days[i].Date)
{
Console.WriteLine("Worked: {0}", dt);
i++;
}
else
{
Console.WriteLine("Not Worked: {0}", dt);
}
}
(This assumes there are no duplicate days in the list.)
Build a list of valid dates and subtract your machine day collection from it using LINQ's Enumerable.Except extension method. Something like this:
IEnumerable<DateTime> dates = get_candidate_dates();
var holidays = dates.Except(machinedays.Select(m => m.WorkingDay));
The get_candidate_dates() method could even be an iterator that generates all dates within a range on the fly, rather than a pre-stored list of all dates.
Enumerable's methods are reasonably smart and will usually do a decent job on the performance side of things, but if you want the fastest possible algorithm, it will depend on how you plan to consume the result.
Sorry dudes, but I do not pretty much like your solutions.
I think you should create a HashTable with your dates. You can do this by interating only once the working days.
Then, you interate the full range of of days and for every one you query in the hashtable if the date is there or not, by using
myHashTable.ContainsKey(day); // this is efficient
Simple, elegant and fast.
I think your solution uses an exponencial time, this one is lineal or logarithmical (which is actually a good thing).
Assuming the list is sorted and the machine was "working" most of the time, you may be able to avoid iterating through all the dates by grouping the dates by month and skipping the dates in between. Something like this (you'll need to clean up):
int chunksize = 60; // adjust depending on data
machineday currentDay = myMachinedaycollection[0];
for (int i = 0; i < myMachinedaycollection.Count; i += chunksize)
{
if (currentDay.WorkingDay.AddDays(chunksize) != myMachinedaycollection[i + chunksize].WorkingDay)
{
// write code to iterate through current chunk and get all the non-working days
}
currentDay = myMachinedaycollection[i + chunksize];
}
I doubt you want a list of days working and not working.
Your question title suggests that you want to know whether the system was up on a particular date. It also seems reasonable to calculate % uptime. Neither of these requires building a list of all time instants in the interval.
Sort the service times. For the first question, do BinarySearch for the date you care about and check whether the preceding entry was the system being taken offline of maintenance or put back into service. For % uptime, take the (down for maintenance, service restored) pair-wise, use subtraction to find the duration of maintenance, add these up. Then use subtraction to find the length of the total interval.
If your question didn't actually mean you were keeping track of maintenance intervals (or equivalently usage intervals) then you can ignore this answer.
So, here's the scenario. I have a file with a created time, and I want to choose a time from a list of times that that file's created time is closest or equal too...what would be the best way to accomplish this?
var closestTime = listOfTimes.OrderBy(t => Math.Abs((t - fileCreateTime).Ticks))
.First();
If you don't want the performance overhead of the OrderBy call then you could use something like the MinBy extension method from MoreLINQ instead:
var closestTime = listOfTimes.MinBy(t => Math.Abs((t - fileCreateTime).Ticks));
Something like this:
DateTime fileDate, closestDate;
ArrayList theDates;
long min = long.MaxValue;
foreach (DateTime date in theDates)
if (Math.Abs(date.Ticks - fileDate.Ticks) < min)
{
min = Math.Abs(date.Ticks - fileDate.Ticks);
closestDate = date;
}
The accepted answer is completely wrong. What you want is something like this:
DateTime fileDate, closestDate;
List<DateTime> theDates;
fileDate = DateTime.Today; //set to the file date
theDates = new List<DateTime>(); //load the date list, obviously
long min = Math.Abs(fileDate.Ticks - theDates[0].Ticks);
long diff;
foreach (DateTime date in theDates)
{
diff = Math.Abs(fileDate.Ticks - date.Ticks);
if (diff < min)
{
min = diff;
closestDate = date;
}
}
var closestTime = (from t in listOfTimes
orderby (t - fileInfo.CreationTime).Duration()
select t).First();
How often will you be doing this with the same list of times? If you're only doing it once, the fastest way is probably to just scan through the list and keep track of the closest time you've seen yet. When/if you encounter a time that's closer, replace the "closest" with that closer time.
If you're doing it very often, you'd probably want to sort the list, then use a binary search.
get the difference of your file creatime and every time in your list and sort the absolute value of each time difference. the first one should be the answer you are looking for.
Use the minimum absolute time difference between the file time and the time in the list. You might get 2 entries being the same, and then you would need a different method to differ between these.
Not an answer, but a question regarding the various LINQ solutions proposed above. How efficient is LINQ? I have not written any "real" programs with LINQ yet, so I'm not sure on the performance.
In this example, the "listOfTimes" collection implies that we have already iterated over some file system based objects to gather the times. Would it have been more efficient to do the analysis during the iteration instead of later in LINQ? I recognize that these solutions may be more "elegant" or nicely abstract the "collection as database" idea, but I tend to choose efficiency (must be readable though) over elagance in my programming. Just wondering if the cost of LINQ might outweigh the elegance here?
var creationTimes = new [] {DateTime.Now.AddDays(-1), DateTime.Now.AddDays(-2)};
FileInfo fi = new FileInfo("C:/test.xml");
var closestTime = creationTimes
.OrderBy(c => Math.Abs(c.Subtract(fi.CreationTime).Days))
.First();
var min = listoftimes.Select(
x => new { diff = Math.Abs((x - timeoffile).Ticks), time = x}).
OrderBy(x => x.diff).
First().time;
Note: Assumes at least 1 entry in listoftimes.
I thought I would update this post to include a real world scenario. I wanted this sort of function as I have a blog showing news of the latest movie screenings.
However I don't want to list screening in the past (ie screening date past the current date) and as I wanted to show a record I needed some sort of ID passed to pick up the record.
I have left if simple so that you can follow the process and no doubt make it more efficient with LINQ et al.
First the model
public class LatestScreeeningsModel
{
public int Id { get; set; }
public DateTime Date { get; set; }
}
Then the code block you can call from your controller
private static LatestScreeeningsModel GetLatestScreening(IPublishedContent currentNode)
{
LatestScreeeningsModel latestScreening = new LatestScreeeningsModel();
DateTime fileDate;
// get a list of screenings that have not shown yet
var screenings = currentNode.AncestorsOrSelf("siteLanguage")
.FirstOrDefault().Descendants("screening")
.Select(x => new LatestScreeeningsModel() { Id = x.Id, Date = x.GetPropertyValue<DateTime>("date") })
.Where(x => x.Date > DateTime.Now).ToList();
fileDate = DateTime.Today;
long min = Math.Abs(fileDate.Ticks - screenings[0].Date.Ticks);
long diff;
foreach (var comingDate in screenings)
{
diff = Math.Abs(fileDate.Ticks - comingDate.Date.Ticks);
if (diff <= min)
{
min = diff;
latestScreening = comingDate;
}
}
return latestScreening;
}
I am using Umbraco to get the date items but it would work with any custom model, List et al.
Hope it helps
This is a generalized solution to the question, "Find the closest time from a list of times". This solution finds the closest time before and after a given search time.
//For finding the closest time in a list using a given search time...
var listOfTimes = new List<DateTime>();
listOfTimes.Add(DateTime.Parse("1/1/2000"));
listOfTimes.Add(DateTime.Parse("1/2/2000"));
listOfTimes.Add(DateTime.Parse("1/3/2000"));
listOfTimes.Add(DateTime.Parse("1/4/2000"));
listOfTimes.Add(DateTime.Parse("1/5/2000"));
var searchTime = DateTime.Parse("1/3/2000");
var closestBefore = listOfTimes.LastOrDefault(t => t < searchTime);
var closestAfter = listOfTimes.FirstOrDefault(t => t > searchTime);
Console.WriteLine(closestBefore);
Console.WriteLine(closestAfter);
/*
searchTime: 1/3/2000
before: 1/2/2000 12:00:00 AM
after: 1/4/2000 12:00:00 AM
searchTime: 1/1/1900 (edge case)
before: 1/1/0001 12:00:00 AM (DateTime.MinValue)
after: 1/1/2000 12:00:00 AM
searchTime: 1/1/2001 (edge case)
before: 1/5/2000 12:00:00 AM
after: 1/1/0001 12:00:00 AM (DateTime.MinValue)
*/