Calculate number of milliseconds until next occurence of a time - c#

I feel like this is something really simple, but my Google Fu is letting me down as I keep finding difference calculations.
I have a time (e.g. 1800 hours) stored in a DateTime object. The date is null and immaterial. All I want to know is how many milliseconds until the NEXT occurrence of that time.
So, if I run the calculation at 0600 - it will return 12 hours (in ms). At 1750, it will return ten minutes (in ms) and at 1900 it will return 24 hours (in ms).
All the things I can find show me how to calculate differences, which doesn't work once you're past the time.
Here is what I tried, but fails once you're past the time and gives negative values:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;

You're already doing everything you should, except for one thing: handling negative results.
If the result is negative, it means the time you want to calculate the duration until has already passed, and then you want it to mean "tomorrow" instead, and get a positive value.
In this case, simply add 24 hours:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result < 0)
result += TimeSpan.FromHours(24).TotalMilliseconds;
The next thing to consider is this: If the time you want to calculate the duration until is 19:00 hours, and the current time is exactly 19:00 hours, do you want it to return 0 (zero) or 24 hours worth of time? Meaning, do you really want the next such occurrence?
If so, then change the above if-statement to use <=:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result <= 0)
result += TimeSpan.FromHours(24).TotalMilliseconds;
However, note that this will be prone to the usual problems with floating point values. If the current time is 18:59:59.9999999, do you still want it to return the current time (a minuscule portion of time) until 19:00 today, or do you want it to flip to tomorrow? If so, change the comparison to be slightly different:
DateTime nowTime = DateTime.Now;
TimeSpan difference = _shutdownTime.TimeOfDay - nowTime.TimeOfDay;
double result = difference.TotalMilliseconds;
if (result <= -0.0001)
result += TimeSpan.FromHours(24).TotalMilliseconds;
where -0.0001 is a value that corresponds to "the range of inaccuracy you're prepared to accept being tomorrow instead of today in terms of milliseconds".

When doing calculations like this it is important to take possible DST changes under consideration so that your results remain correct.
Suppose your operational parameters are:
var shutdownTime = TimeSpan.FromHours(18);
// just to illustrate, in Europe there is a DST change on 2013-10-27
// normally you 'd just use DateTime.Now here
var now = new DateTime(2013, 10, 26, 20, 00, 00);
// do your calculations in local time
var nextShutdown = now.Date + shutdownTime;
if (nextShutdown < now) {
nextShutdown = nextShutdown.AddDays(1);
}
// when you want to calculate time spans in absolute time
// (vs. wall clock time) always convert to UTC first
var remaining = nextShutdown.ToUniversalTime() - now.ToUniversalTime();
Console.WriteLine(remaining);
The answer to your question would now be remaining.TotalMilliseconds.

Related

How to get the total hours with different dates in C#

This is my problem now, i want to get the total hours and mins of work.
example from jan. 11 2017 22:00 to Jan. 12 2017 7:00.
so far i have it only work when the end date is not change
DateTime pin = today, pout = DateTime.Parse(empTime);
TimeSpan spanMe = pout.Subtract(pin);
spanMe.Hours
spanMe.Minutes
it gives me negative numbers.
it gives me negative numbers.
That is expected if you subtract a larger item from a smaller item (ie. subtracting a more recent time from an older time). If you always want to see the difference as a positive number and do not want to take into account which is larger then wrap the result of the properties (like .Hours) in Math.Abs (absolute value).
var hours = System.Math.Abs(spanMe.Hours);
var minutes = System.Math.Abs(spanMe.Minutes);
Also as pointed out by #stuartd there is a difference between Hours/Minutes and TotalHours/TotalMinutes. Make sure you are using the correct one for your needs.
It should work :
DateTime pin = DateTime.Parse("jan 11 2017 22:00");
DateTime pout = DateTime.Parse("Jan 12 2017 7:00");
TimeSpan spanMe = pout.Subtract(pin);
Console.WriteLine("Hours : {0}, Minutes : {1}", spanMe.Hours, spanMe.Minutes);
Console.ReadLine();
if you know what is the latest date, you need arrange it accordingly. If not, you can not multiply by -1:
double GetHouers(DateTime one, DateTime another)
{
var diff = (one - another).TotalHours;
return diff > 0 ? diff : diff * -1;
}
You can subtract one DateTime Object from another, and then use .TotalHours property of the DateTime class to get the number of hours. It will give you a double value representing the total hours.
DateTime pin = today, pout = DateTime.Parse(empTime);
double hours = (pout - pin).TotalHours;
DateTime pin = today, pout = DateTime.Parse(empTime);
TimeSpan spanMe = pin.Subtract(pout);
var hours = spanMe.TotalHours;
var minutes = spanMe.TotalMinutes;
You want to use TotalHours and TotalMinutes as these will handle fractions thereof, versus Hours and Minutes which return only whole values. You also need to swap the order of your operands as above for the subtraction step.

How to handle daylight saving when using the Outlook C# GetFreeBusy() API?

I have an Outlook VSTO addin and I am doing search for resource calendar availability using the GetFreeBusy() API Calll which, given a date, will search over the next 28 days in 30 minute increments (by default) to determine which slots are free and which are busy. It works fine except I am struggling to figure out how to cope with the situation where a daylight savings time exists within that 28 day interval.
Here is my code:
using Microsoft.Office.Interop.Outlook;
string freeBusy = exchangeUser.GetFreeBusy(startDate, 30, true);
this gives me back a string like this that returns free / busy availability in 30 minute increments for 28 days.
0000000000000000202222222222000000000000000000000000000000000222222
this string is always 1344 characters long (48 slots per day * 28 days)
where each character represents a 30 minute slot and shows 0 if the time is free. I have the following parsing code that I took from this Microsoft article that returns an array of free time slots:
private IEnumerable<DateTime> ParseFreeBusy(string freeBusyString, DateTime startingDate)
{
var timeSlots = new List<DateTime>();
for (int i = 0; i < freeBusyString.Length; i++)
{
double slot = i * 30;
DateTime timeSlot = startingDate.Date.AddMinutes(slot);
bool isFree = freeBusy.Substring(i, 1) == "0";
if (isFree)
{
timeSlots.Add(timeSlot);
}
}
return timeSlots;
}
If I plug in October 25th as the start date when I look at the results every thing lines up perfectly up until November 2nd at 2AM (given daylight savings)
The root issue is that my naive code is simply increments and keeps adding 30 minutes for each entry since I am simply looping through each slot and doing this:
startingDate.Date.AddMinutes(slot);
I did a test and booked a calendar slot from 1AM - 2AM on November 2nd and this is what i get from GetFreeBusy() starting on that day
002222000...
so using the default loop above (remember, every character is a 30 min slot and 0 = free), this would translate to the following slot logic:
12L00 AM - free (0)
12:30 AM - free (0)
1L00 AM - booked (2)
1:30 AM - booked (2)
THESE NEXT TWO "booked" below is really representing the 2nd 1AM - 2AM since we roll the clocks back an hour
2:00 AM - booked (2)
2:30 AM - booked (2)
3:00 AM - free (0)
which is wrong as my code would show 2AM - 3AM booked when the "real" 2-3A AM is free. If my parsing was correct and handled this rollback, I would end up with this correct answer of:
12L00 AM - free (0)
12:30 AM - free (0)
1L00 AM - booked (2)
1:30 AM - booked (2)
IGNORE the second 1AM to 2AM as its already taken care of
2:00 AM - free (0)
2:30 AM - free (0)
3:00 AM - free (0)
What is interesting is that regardless of daylight savings, the resulting string is always 1344 characters long (I would have expected it to be shorter or longer on those months with daylight savings implications).
Does anyone have any experience with using outlook GetFreeBusy() and understand for how to deal with this situation when you hit a daily savings time slot?
I have been playing around with a few ideas like:
var tzInfo = TimeZoneInfo.Local;
if (tzInfo.IsAmbiguousTime(timeSlot))
{
//this would be a time to do something
}
or something like
DaylightTime daylightTime = tz.GetDaylightChanges(minStartTime.Year);
if (daylightTime.End == proposedTimeSlot)
{
daylightSavingsOffset = daylightSavingsOffset + ((daylightTime.Delta.Hours * 60) / meetingDuration);
}
but I am not completely sure what do with this once i detect the "special slots" and I can't find any documentation or recommendations around this situation.
Any suggestions?
What is interesting is that regardless of daylight savings, the resulting string is always 1344 characters long (I would have expected it to be shorter or longer on those months with daylight savings implications).
It's completely logical, Let's start with GetFreeBusy, It happens because the result is based on duration and specific intervals not Date and time stamps, and as we know Date and Time is relative to our location based on time zone but elapsed time and duration is not,let's assume we have meeting in 10 hours from now, we maybe are in different time Zones but after 10 hours (relative to our location) we both should meet each other, but our local times may vary significantly, the system works this way because it should be able to operate across different time zones at the same time, so it uses UniversalTime at the heart and converts it back to local time for generating the result.
Now let's check the code, when we use startingDate.Date.AddMinutes(slot); we are not considering DateTimeSaving as we are operating on our local time and the addition is relative to it, by using UniversalTime we can create a unified base point for our time additions and intervals, after that by converting it back to local time we can apply date time saving to it,
so I believe this code should work as expected:
private static IEnumerable<DateTime> ParseFreeBusy(string freeBusyString, DateTime startingDate)
{
var timeSlots = new HashSet<DateTime>();
var utc = startingDate.ToUniversalTime();
var timeZone = TimeZone.CurrentTimeZone; //can change to particular time zone, currently set to local timezone of the system
for (int i = 0; i < freeBusyString.Length; i++)
{
double slot = i * 30;
DateTime timeSlot = utc.AddMinutes(slot);
bool isFree = freeBusyString.Substring(i, 1) == "0";
if (isFree)
{
var localTimeSlot = timeZone.ToLocalTime(timeSlot);
timeSlots.Add(localTimeSlot);
}
}
return timeSlots;
}
NOTE:: beside using UTC for time, I changed the List to HashSet because if you have free slot on those specific times you would get duplicate entries, by using HashSet this problem won't occur.
here is a method I used for testing it:
private static void TestFreeSlots()
{
var saving = TimeZone.CurrentTimeZone.GetDaylightChanges(DateTime.Now.Year);
var datetime = new DateTime(saving.End.Year, saving.End.Month, saving.End.Day - 1);
//you may need to change the string to see effective result
var result = ParseFreeBusy("0000000000000000000000000000000000000000000000002222000", datetime);
}
and finally here is a little sample to demonstrate the method used here
private static void TestTimeZone()
{
var saving = TimeZone.CurrentTimeZone.GetDaylightChanges(DateTime.Now.Year);
var datetime = new DateTime(saving.End.Year, saving.End.Month, saving.End.Day - 1);
var utc = datetime.ToUniversalTime();
var timeZone = TimeZone.CurrentTimeZone;
for (var i = 0; i < 120; i++)
{
var next = timeZone.ToLocalTime(utc);
Console.WriteLine(next);
utc = utc.AddMinutes(30);
}
}
and your results should be similar to this:
This first function finds time slots that Outlook will return as duplicates due to DST. It can probably stand some refactoring but it's effective for now: (EDIT: I modified the function so it doesn't remove time slots as you go into DST).
public static Collection<DateTime> GetDuplicateSlots(
TimeZoneInfo timeZone, DateTime start, int intervalLength, int numOfIntervals)
{
Collection<DateTime> duplicates = new Collection<DateTime>();
bool dstAtStart = timeZone.IsDaylightSavingTime(start);
for (int interval = 0; interval < numOfIntervals; interval++)
{
DateTime current = start.Date.AddMinutes(interval * intervalLength);
if (dstAtStart && !timeZone.IsDaylightSavingTime(current))
{
duplicates.Add(current);
duplicates.Add(current.AddMinutes(intervalLength));
return duplicates;
}
}
return duplicates; // no duplicates
}
Then we just need to adjust for the duplicates when we go through the string of free/busy time slots:
public static void DisplayFreeBusy(
string freeBusyString, DateTime start, int intervalLength)
{
TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
Collection<DateTime> duplicateSlots =
GetDuplicateSlots(cst, start, intervalLength, freeBusyString.Length);
int duplicatesConsumed = 0;
for (int slot = 0; slot < freeBusyString.Length; slot++)
{
int actualSlot = slot - duplicatesConsumed;
DateTime slotTime = start.Date.AddMinutes(actualSlot * intervalLength);
if (duplicatesConsumed != duplicateSlots.Count &&
duplicateSlots.Contains(slotTime))
{
duplicatesConsumed++;
}
else
{
Console.WriteLine("{0} -- {1}", slotTime, freeBusyString[slot]);
}
}
}
Note that the actualSlot variable corresponds to the time slots, while the slot variable still corresponds with a character in the free/busy string. When a duplicate is found, it is "consumed" and that character in the string is skipped. Once the duplicates have been consumed, the function will continue normally from that point.
I live in Arizona and we don't have DST so I had to force a different time zone. You can obviously substitute your local time zone instead of CST.
I tested this with a shorter input string but I added the extra '2' characters for the daylight savings slots. It handled the excess slots and prints out the proper number of slots.

Converting a double to a time that makes sense

Basically I am setting a limit of hours a user can use. Now every time a button is press, whatever time that person accrued gets taken away from this total value.
However because a limit would be represented as say 156 hours, and the datetime representation of 5 minutes would be 00.05 the result would be 155.95, rather than 155.55 .
I work this out like so
string date2 = TotalMonthlyHours.ToString("HH.mm");
double date = double.Parse(date2);
RunningTotal = date + RunningTotal;
Total = limit - RunningTotal;
Any ideas?
I think you are trying to represent 5 minutes as 0.05. The way to do that is to first of all obtain the minutes as an integer. And then simply convert to double.
double floatmins = minutes/100.0;
And you convert in the other direction like this:
int minutes = (int) (floatmins*100.0);
However, I urge you not to go any further with this. You cannot expect to perform arithmetic on a quantity like that. What is the result of 2.20-1.50? You and I know it's 30 minutes, but the computer says 0.70 which is no use at all.
Store the hours using a true fractional representation. So 5 minutes is 5/60.0. Or store the total minutes in an integer. Or total seconds in an integer. Or a TimeSpan.
The key is that you can write your own helper routines to convert from a sane storage format to a value that is human readable. But you must store the raw data in a representation that will admit arithmetic operations.
I think I worked it out by doing something like this
string[] times = date.ToString().Split('.');
if (date != 0.0)
{
string minutesString = times[1];
string hoursString = times[0];
double minutes = Convert.ToDouble(minutesString);
double hours = Convert.ToDouble(hoursString);
// end of splitting
TimeSpan Limit = TimeSpan.FromHours(limit);
TimeSpan Hours = TimeSpan.FromHours((int)hours);
TimeSpan Minutes = TimeSpan.FromMinutes((int)minutes);
TimeSpan SubTotal = Hours + Minutes;
Time = Limit - SubTotal;
}
Edit: Glad you came up with the same as me,Just read your reply David, let's hope it works
I would convert it to minutes first than add as minutes to the date
var min = Convert.ToDouble(Convert.ToDecimal(textbox.Text) * 60);
DateTimePickerEnd.DbSelectedDate = e.NewDate.Value.AddMinutes(min);

How to convert year, month and day to ticks without using DateTime

long ticks = new DateTime(2012, 1, 31).ToLocalTime().Ticks; // 634635684000000000
But how to do this without DateTime constructor ?
edit
What I actually want is to keep only the years, months and days from the ticks.
long ALL_Ticks = DateTime.Now.Ticks; // 634636033446495283
long Only_YearMonthDay = 634635684000000000; // how to do this ?
I want to use this in a linq-sql query using Linq.Translations.
If you only want the ticks for the date portion of the current datetime you could use:
long Only_YearMonthDay = DateTime.Now.Date.Ticks; //634635648000000000
//DateTime.Now.Date.Ticks + DateTime.Now.TimeOfDay.Ticks == DateTime.Now.Ticks
You could find out how many days are in the calculation and then multiply by 864,000,000,000 (which is how many ticks are in a day). Is that what you are looking for? Bit of documentation here : http://msdn.microsoft.com/en-us/library/system.timespan.ticksperday.aspx.
Happy coding,
Cheers,
Chris.
OK - didn't think this through properly! Ticks represent the amount of 100 nanosecond intervals since 12:00:00 midnight, January 1, 0001. You would need to calculate how many days have passed since that date and then multiply it by the ticks per day value!
If I understand you right, you are not worried about the ticks up to a particular time of the day?! So, it would be something along the lines of :
var ticksToDate = (DateTime.UtcNow - DateTime.MinValue).Days * 864000000000;
Does that answer your question??
That is going to be rather difficult unless you have some other way of getting the current date and time. According to MSDN:
A single tick represents one hundred nanoseconds or one ten-millionth of a second. There are 10,000 ticks in a millisecond.
The value of this property represents the number of 100-nanosecond intervals that have elapsed since 12:00:00 midnight, January 1, 0001, which represents DateTime.MinValue. It does not include the number of ticks that are attributable to leap seconds.
Now, if you know the current date and time, you can calculate how many days have passed since January 1, 0001 and use that to calculate the number of ticks.
I understand you dont want the hour parts of the date. If you use Date, then you only get the day (for example: 01/01/2012 00:00:00)
long ticks = new DateTime(2012, 1, 31).Date.Ticks;
And with any DateTime object already created is the same of course.
long ticks = dateObject.Date.Ticks;
You already have the answer there in your post:
long ALL_Ticks = DateTime.Now.Ticks;
// that's the ticks (from DateTime.MinValue) until 'now' (this very moment)
long ticks = new DateTime(2012, 1, 31).ToLocalTime().Ticks;
// or
long ticks = DateTime.Now.Date.Ticks;
// that's the ticks until the beginning of today
long yearmonthticks = new DateTime(2012, 1, 1).ToLocalTime().Ticks;
// that's the ticks until the beginning of the month
// etc..., the rest is simple subtractions
Since your question doesn't specify any reason not to use the DateTime constructor, this is the best solution for what seems like your problem.
I had a use case where I couldn't use DateTime but needed Years/Months from Ticks.
I used the source behind DateTime to figure out how. To go the other way you can look at the constructor, one of which calls the following code.
private static long DateToTicks(int year, int month, int day) {
if (year >= 1 && year <= 9999 && month >= 1 && month <= 12) {
int[] days = IsLeapYear(year)? DaysToMonth366: DaysToMonth365;
if (day >= 1 && day <= days[month] - days[month - 1]) {
int y = year - 1;
int n = y * 365 + y / 4 - y / 100 + y / 400 + days[month - 1] + day - 1;
return n * TicksPerDay;
}
}
throw new ArgumentOutOfRangeException(null, Environment.GetResourceString("ArgumentOutOfRange_BadYearMonthDay"));
}
This can be found in link below, of course you will need to re-write to suit your needs and look up the constants and IsLeapYear function too.
https://referencesource.microsoft.com/#mscorlib/system/datetime.cs,602

Calculating the elapsed working hours between 2 datetime

Given two datetimes. What is the best way to calculate the number of working hours between them. Considering the working hours are Mon 8 - 5.30, and Tue-Fri 8.30 - 5.30, and that potentially any day could be a public holiday.
This is my effort, seem hideously inefficient but in terms of the number of iterations and that the IsWorkingDay method hits the DB to see if that datetime is a public holiday.
Can anyone suggest any optimizations or alternatives.
public decimal ElapsedWorkingHours(DateTime start, DateTime finish)
{
decimal counter = 0;
while (start.CompareTo(finish) <= 0)
{
if (IsWorkingDay(start) && IsOfficeHours(start))
{
start = start.AddMinutes(1);
counter++;
}
else
{
start = start.AddMinutes(1);
}
}
decimal hours;
if (counter != 0)
{
hours = counter/60;
}
return hours;
}
Before you start optimizing it, ask yourself two questions.
a) Does it work?
b) Is it too slow?
Only if the answer to both question is "yes" are you ready to start optimizing.
Apart from that
you only need to worry about minutes and hours on the start day and end day. Intervening days will obviously be a full 9/9.5 hours, unless they are holidays or weekends
No need to check a weekend day to see if it's a holiday
Here's how I'd do it
// Normalise start and end
while start.day is weekend or holiday, start.day++, start.time = 0.00am
if start.day is monday,
start.time = max(start.time, 8am)
else
start.time = max(start.time, 8.30am)
while end.day is weekend or holiday, end.day--, end.time = 11.59pm
end.time = min(end.time, 5.30pm)
// Now we've normalised, is there any time left?
if start > end
return 0
// Calculate time in first day
timediff = 5.30pm - start.time
day = start.day + 1
// Add time on all intervening days
while(day < end.day)
// returns 9 or 9.30hrs or 0 as appropriate, could be optimised to grab all records
// from the database in 1 or 2 hits, by counting all intervening mondays, and all
// intervening tue-fris (non-holidays)
timediff += duration(day)
// Add time on last day
timediff += end.time - 08.30am
if end.day is Monday then
timediff += end.time - 08.00am
else
timediff += end.time - 08.30am
return timediff
You could do something like
SELECT COUNT(DAY) FROM HOLIDAY WHERE HOLIDAY BETWEEN #Start AND #End GROUP BY DAY
to count the number of holidays falling on Monday, Tuesday, Wednesday, and so forth. Probably a way of getting SQL to count just Mondays and non-Mondays, though can't think of anything at the moment.
especially considering the IsWorkingDay method hits the DB to see if that day is a public holiday
If the problem is the number of queries rather than the amount of data, query the working day data from the data base for the entire day range you need at the beginning instead of querying in each loop iteration.
There's also the recursive solution. Not necessarily efficient, but a lot of fun:
public decimal ElapseddWorkingHours(DateTime start, DateTime finish)
{
if (start.Date == finish.Date)
return (finish - start).TotalHours;
if (IsWorkingDay(start.Date))
return ElapsedWorkingHours(start, new DateTime(start.Year, start.Month, start.Day, 17, 30, 0))
+ ElapsedWorkingHours(start.Date.AddDays(1).AddHours(DateStartTime(start.Date.AddDays(1)), finish);
else
return ElapsedWorkingHours(start.Date.AddDays(1), finish);
}
Take a look at the TimeSpan Class. That will give you the hours between any 2 times.
A single DB call can also get the holidays between your two times; something along the lines of:
SELECT COUNT(*) FROM HOLIDAY WHERE HOLIDAY BETWEEN #Start AND #End
Multiply that count by 8 and subtract it from your total hours.
-Ian
EDIT: In response to below, If you're holiday's are not a constant number of hours. you can keep a HolidayStart and a HolidayEnd Time in your DB and and just return them from the call to the db as well. Do an hour count similar to whatever method you settle on for the main routine.
Building on what #OregonGhost said, rather than using an IsWorkingDay() function at accepts a day and returns a boolean, have a HolidayCount() function that accepts a range and returns an integer giving the number of Holidays in the range. The trick here is if you're dealing with a partial date for your boundry beginning and end days you may still need to determine if those dates are themselves holidays. But even then, you could use the new method to make sure you needed at most three calls the to DB.
Try something along these lines:
TimeSpan = TimeSpan Between Date1 And Date2
cntDays = TimeSpan.Days
cntNumberMondays = Iterate Between Date1 And Date2 Counting Mondays
cntdays = cntdays - cntnumbermondays
NumHolidays = DBCall To Get # Holidays BETWEEN Date1 AND Date2
Cntdays = cntdays - numholidays
numberhours = ((decimal)cntdays * NumberHoursInWorkingDay )+((decimal)cntNumberMondays * NumberHoursInMondayWorkDay )
Use #Ian's query to check between dates to find out which days are not working days. Then do some math to find out if your start time or end time falls on a non-working day and subtract the difference.
So if start is Saturday noon, and end is Monday noon, the query should give you back 2 days, from which you calculate 48 hours (2 x 24). If your query on IsWorkingDay(start) returns false, subtract from 24 the time from start to midnight, which would give you 12 hours, or 36 hours total non-working hours.
Now, if your office hours are the same for every day, you do a similar thing. If your office hours are a bit scattered, you'll have more trouble.
Ideally, make a single query on the database that gives you all of the office hours between the two times (or even dates). Then do the math locally from that set.
The most efficient way to do this is to calculate the total time difference, then subtract the time that is a weekend or holiday. There are quite a few edge cases to consider, but you can simplify that by taking the first and last days of the range and calculating them seperately.
The COUNT(*) method suggested by Ian Jacobs seems like a good way to count the holidays. Whatever you use, it will just handle the whole days, you need to cover the start and end dates separately.
Counting the weekend days is easy; if you have a function Weekday(date) that returns 0 for Monday through 6 for Sunday, it looks like this:
saturdays = ((finish - start) + Weekday(start) + 2) / 7;
sundays = ((finish - start) + Weekday(start) + 1) / 7;
Note: (finish - start) isn't to be taken literally, replace it with something that calculates the time span in days.
Dim totalMinutes As Integer = 0
For minute As Integer = 0 To DateDiff(DateInterval.Minute, contextInParameter1, contextInParameter2)
Dim d As Date = contextInParameter1.AddMinutes(minute)
If d.DayOfWeek <= DayOfWeek.Friday AndAlso _
d.DayOfWeek >= DayOfWeek.Monday AndAlso _
d.Hour >= 8 AndAlso _
d.Hour <= 17 Then
totalMinutes += 1
Else
Dim test = ""
End If
Next minute
Dim totalHours = totalMinutes / 60
Piece of Cake!
Cheers!

Categories