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.
Related
DateTime dtLastUse = date1.Subtract(date2);
Long lSubtract = dtLastUse.Ticks;
The Result I Get:
My Result return something like this { 14433.14:02:30 }
How to return me only 14434?
The result of subtracting a date from another date is a TimeSpan. This is to prevent exactly the kind of confusion you're having. If you want to get the total number of days, use TotalDays. You can round that value the way you want (e.g. use Math.Floor if you want the number of complete days) and cast to int to get an integer value.
Just because Ticks has the same data type you want doesn't mean it's what you want. It essentially gives you the full time resolution possible of the data in the TimeSpan (which happens to be tenths of microseconds).
I have a query with how datetimes are compared/stored in C#. Consider the following code:
var createdDate = DateTime.Now;
using (cr = new LanguageDictionaryRepository(ds)) {
cr.Add(new Sybrin10.Data.DTO.LanguageDictionary() {
Active = true,
CreatedDate = createdDate,
CultureCode = cultureCode,
Data = new System.Text.UTF8Encoding().GetBytes("Test")
});
cr.Save();
var y = cr.FindBy(x => x.CultureCode == cultureCode && x.CreatedDate == createdDate).FirstOrDefault();
Assert.IsNotNull(y);
The Assert.IsNotNull is failing because of the datetime check. I would expect that as the LanguageDictionary instance is created with the variable's value that the two would be equal. This is using Telerik.OpenAccess and MSSQL as a DB layer so I'm assuming the problem comes in there. Can anyone tell me if there is anything I'm missing with this and how to correctly compare these values.
EDIT: The tick values are different but I don't know why as they both come from the same variable which I only assign to once.
Try using DateTime.Equals(x.CreatedDate, createdDate), it might help.
Other than that, proper DateTime comparing is a massively complicated subject with timezones, offsets, utc, local time and whatnot. I wouldn't at all be suprised at a simple == compare between two seemingly identical dates to return false.
If the Ticks value differs on write and read, you're might be facing a DateTimeKind problem, where you're writing a DateTimeKind.Local to the database, but getting back an DateTimeKind.Unspecified.
The other option could be (if the difference is small enough) that the DateTime field in your database is not significant enough to store the same amount of milliseconds as the .net DateTime:
A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
Depending on your data storage, it might not be as detailed as this. Your DateTime values do not come from the same source, one is read from memory, the other is read from database.
SqlServer stores a datetime in (about) 3-millisecond increments.
datetime values are rounded to increments of .000, .003, or .007 seconds
A roundtrip of a DateTime through the database could thus be off by a few ms.
So you should not test for "exactly equal", but for "close enough"
var y = cr.FindBy(x => x.CultureCode == cultureCode &&
x.CreatedDate >= createdDate.AddMilliseconds(-5) &&
x.CreatedDate <= createdDate.AddMilliseconds(5))
.FirstOrDefault();
Late edit: an extension method
public static class Extensions
{
public static bool IsAboutEqualTo(this DateTime target, DateTime other)
=> Math.Abs((target - other).TotalMilliseconds) <= 4;
}
Usage
var y = cr.FindBy(x => x.CultureCode == cultureCode &&
x.CreatedDate.IsAboutEqualTo(createdDate))
.FirstOrDefault();
Do note that Entity Framework will not be able to translate this into SQL, it will only work in linq-to-objects.
I think it might be better for you to use DateTime.UtcNow when you store the data and then you don't have to worry about daylight saving time issues etc. You can then display it how you want later using the culture you pick.
Is there any kind of mathematical way to cut DateTime down to a exact Hour, Day or so? Similiar to round of a decimal to int.
Period.Day
If the original value was 2011-01-01 13:00:00, it ends up in 2011-01-01 00:00:00
if Period.Hour
If the original value was 2011-03-11 13:32:00, it ends up in 2011-03-11 13:00:00
I think about something like below. This are of course works fine, but the range-array are iterated through anyway, later. Better if I was possible to calculate directly on that iteration, instead of it's own. But someType can't be put into that iteration (it depends on someType).
if (someType == Period.Day)
range.ForEach(d => d.time = new DateTime(d.time.Year, d.time.Month, d.time.Day,0,0,0));
if (someType == Period.Hour)
range.ForEach(d => d.time = new DateTime(d.time.Year, d.time.Month, d.time.Day, d.time.Hour, 0, 0));
Rounding down to a day is equivalent to time.Date, rounding to nearest (up on midpoint) is simply ( time + 12hours ).Date.
For rounding down to a full hour I can't think of code that's nicer to read than yours. For rounding up to the nearest hour you can apply your code to time + 30mins.
There is probably a faster method for rounding to the nearest hour:
const Int64 HourInTicks=...;
Int64 timeInTicks=time.Ticks;
Int64 trucatedToHour=timeInTicks-timeInTicks%HourInTicks;
But I'd avoid that, unless you really need the performance, which is unlikely.
(My round to nearest might have issues on days where the local time offset changes if you're using local time)
To round down to day you can use the DateTime.Date Property.
To round down to hour, I'm afraid you'll have to either use what you did in your example or something like:
d.Date.AddHours(d.Hour)
I'll do the following:
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
public static DateTime Round(this DateTime d, Period p)
{
var ts = d - Epoch;
if (p == Period.Hour)
{
var hours = (long)ts.TotalHours;
return Epoch.AddHours(hours);
}
else if (p == Period.Days)
{
var days = (long)ts.TotalDays;
return Epoch.AddDays(days);
}
// ...
}
I believe the following C# code will round a DateTime value to nearest minute, and I think it will be easy to generalize it to round to other units.
//round to nearest minute; add 30 seconds for rounding to nearest minute
effectiveDateTime = effectiveDateTime.AddSeconds(30);
TimeSpan timeComponent = effectiveDateTime.TimeOfDay;
effectiveDateTime = effectiveDateTime.Date;
effectiveDateTime = effectiveDateTime.AddHours(timeComponent.Hours).
AddMinutes(timeComponent.Minutes);
Not sure if this approach is effective, but looks quite nice using string format (in this case cutting down to hours):
var date = DateTime.UtcNow;
var cutDownDate = Convert.ToDateTime(date.ToString("yyyy-MM-dd hh"));
Answer Is there a better way in C# to round a DateTime to the nearest 5 seconds? contains an excellent generic DateTime rounding approach.
Edit:
This answer was before the updated question title and is an algorithm for rounding to nearest not rounding down.
Best method for day:
DateTime now = DateTime.Now;
DateTime roundDay = now.Date;
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.
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.