Trying to find the largest cohesive time for non-working-hours diff in a specific range.
The range that is looked at is defined by:
sDate = range start
eDate = range end
in this defined range, there could be existing registrations (orange blocks in image).
Could someone please give me some input on how to solve this issue.
This code will not compare existing post with each other.
public TimeSpan TimeNightRest(DateTime WhatDay, int dayRange, DataTable TimeData)
{
DateTime DayRangeStart = new DateTime(WhatDay.Year, WhatDay.Month, WhatDay.Day, 0, 0, 0);
DateTime DayRangeEnd = new DateTime(WhatDay.Year, WhatDay.Month, WhatDay.AddDays(dayRange).Day, 0, 0, 0);
TimeSpan nightRest = new TimeSpan();
foreach (DataRow drUnfiltered in TimeData.Rows)
{
DateTime sDate = DateTime.Parse(TimeData.Rows[0]["sDate"].ToString());
DateTime eDate = DateTime.Parse(TimeData.Rows[0]["eDate"].ToString());
//1. db-post cover range
if (sDate < WhatDay && eDate > WhatDay.AddDays(dayRange))
{
nightRest = TimeSpan.FromHours(0);
break;
}
//2. Post exists, start outside ends inside range
if(sDate < WhatDay && eDate < WhatDay.AddDays(dayRange))
{
nightRest = (WhatDay.addDays(dayRange) - eDate)
//More posts could exists that lower this value!!!
}
//3. Post exists, start inside ends outside range
if ((sDate > WhatDay && sDate <WhatDay.AddDays(dayRange)) && eDate > WhatDay.AddDays(dayRange))
{
nightRest = (sDate - WhatDay);
//More posts could exists that lower this value!!!
}
}
return nightRest;
}
I tried to write a different version, this code will compare different post trying to find the largest diff between them. But will fail to find posts that overlaps (no diff) and it will also fail with finding the correct diff with post that starts outside and ends inside the range and opposite.
`
///<Alternative></Alternative>
//1. - No Data Rest = 24h x DayRange
if (timeData.Rows.Count == 0)
{
nightRest = TimeSpan.FromHours(24 * dayRange);
}
///<Alternative></Alternative>
//2. - One Post, Compare to day start/end time
if (timeData.Rows.Count == 1)
{
DateTime sDate = Convert.ToDateTime(timeData.Rows[0]["sDate"].ToString());
DateTime eDate = Convert.ToDateTime(timeData.Rows[0]["eDate"].ToString());
TimeSpan range1 = (sDate - DayRangeStart);
TimeSpan range2 = (DayRangeEnd - eDate);
if (range1 > range2)
{
nightRest = range1;
}
else
{
nightRest = range2;
}
}
///<Alternative></Alternative>
//3. - If DataTable containes more than 1 post, then loop through to calculate diffrence
between post and day start/end
if (timeData.Rows.Count > 1)
{
int i = 1;
int RowCount = timeData.Rows.Count;
foreach (DataRow dr in timeData.Rows)
{
DateTime sDate = Convert.ToDateTime(dr["sdate"]);
DateTime eDate = Convert.ToDateTime(dr["edate"]);
DateTime prevPostSdate = new DateTime();
DateTime nextPostSdate = new DateTime();
//Only 1st time in loop
if (i != 1)
{
//Prev row
DataRow lastRow = timeData.Rows[(i - 1) - 1];
prevPostSdate = Convert.ToDateTime(lastRow["edate"]);
//prevPostEdate = Convert.ToDateTime(lastRow["edate"]);
}
else
{
prevPostSdate = DayRangeStart;
}
//If we are on EOF-post then dont get next post value, get instead range end value
if (i != RowCount)
{
//Next row
DataRow nextRow = timeData.Rows[(i - 1) + 1];
nextPostSdate = Convert.ToDateTime(nextRow["sdate"]);
}
else
{
nextPostSdate = DayRangeEnd;
}
///<Compare>Type #1
///Sdate inside && eDate inside
///</ Compare >
if (DayRangeStart < sDate && DayRangeEnd > eDate)
{
//Compair Range with Post-sdate & Post
//1. Range with Post-sdate
TimeSpan value1 = (sDate - prevPostSdate);
//2. Post-eDate with NextPostSdate
TimeSpan value2 = (nextPostSdate - eDate);
if (value1 > nightRest) nightRest = value1;
if (value2 > nightRest) nightRest = value2;
}
///<Compare>Type #2
///Sdate outside && eDate inside
///</ Compare >
if (DayRangeStart >= sDate && DayRangeEnd > eDate)
{
//Compair Range with Post-sdate & Post
//1. Range with Post-sdate
TimeSpan value1 = (nextPostSdate - eDate);
if (value1 > nightRest) nightRest = value1;
}
///<Compare>Type #3
///Sdate indise && eDate outside
///</ Compare >
if (DayRangeStart < sDate && DayRangeEnd <= eDate)
{
//Compare Range Post-sdate & Post
//1. Range with Post-sdate
TimeSpan value1 = (sDate - prevPostSdate);
//Console.WriteLine(value1);
if (value1 > nightRest) nightRest = value1;
}
///<Compare>Type #4
///Sdate outside && eDate outside
///</ Compare >
if (DayRangeStart >= sDate && DayRangeEnd <= eDate)
{
nightRest = TimeSpan.FromHours(0);
}
i++;
}
}
`
Well i manage to solve this. Probably some much better code out there but this does the work!
public DataTable FilterTable(DataTable table, DateTime startDate, DateTime endDate)
{
var filteredRows =
from row in table.Rows.OfType<DataRow>()
where (DateTime)row["sDate"] < endDate && startDate <= (DateTime)row["eDate"]
select row;
var filteredTable = table.Clone();
filteredRows.ToList().ForEach(r => filteredTable.ImportRow(r));
return filteredTable;
}
public TimeSpan TimeNightRest(DateTime WhatDay, int dayRange, DataTable TimeData)
{
DateTime DayRangeStart = DateTime.Parse(WhatDay.ToShortDateString());
DateTime DayRangeEnd = DateTime.Parse(DayRangeStart.AddDays(dayRange).ToShortDateString());
TimeSpan nightRest = new TimeSpan();
DataTable dtTime = FilterTable(TimeData, WhatDay, WhatDay.AddDays(dayRange));
//1. - No posts
if (dtTime.Rows.Count == 0)
{
nightRest = TimeSpan.FromHours(24 * dayRange);
}
//2. - One post Exists!
if (dtTime.Rows.Count == 1)
{
DateTime sDate = Convert.ToDateTime(dtTime.Rows[0]["sDate"].ToString());
DateTime eDate = Convert.ToDateTime(dtTime.Rows[0]["eDate"].ToString());
if (sDate < DayRangeEnd && DayRangeStart <= eDate)
{
TimeSpan range1 = (sDate - DayRangeStart);
TimeSpan range2 = (DayRangeEnd - eDate);
if (range1 > range2)
{
nightRest = range1;
}
else
{
nightRest = range2;
}
//Negative TimeSpans is ==> Zero
if(nightRest < TimeSpan.Zero)
{
nightRest = TimeSpan.FromHours(0);
}
}
else
{
nightRest = TimeSpan.FromHours(24 * dayRange);
}
}
//3. More then 1 post i db
if (dtTime.Rows.Count > 1)
{
int i = 1;
int RowCount = dtTime.Rows.Count;
foreach (DataRow dr in dtTime.Rows)
{
DateTime sDate = Convert.ToDateTime(dr["sdate"]);
DateTime eDate = Convert.ToDateTime(dr["edate"]);
if (sDate < DayRangeEnd && DayRangeStart <= eDate)
{
DateTime prevPostSdate = new DateTime();
DateTime nextPostSdate = new DateTime();
//1st loop
if (i != 1)
{
//Prev row
DataRow lastRow = dtTime.Rows[(i - 1) - 1];
prevPostSdate = Convert.ToDateTime(lastRow["edate"]);
}
else
{
prevPostSdate = DayRangeStart;
}
//If we are on EOF-post then dont get next post value, get instead range end value
if (i != RowCount)
{
//Next row
DataRow nextRow = dtTime.Rows[(i - 1) + 1];
nextPostSdate = Convert.ToDateTime(nextRow["sdate"]);
}
else
{
nextPostSdate = DayRangeEnd;
}
if (DayRangeStart < sDate && DayRangeEnd > eDate)
{
TimeSpan value1 = (sDate - prevPostSdate);
TimeSpan value2 = (nextPostSdate - eDate);
if (value1 > nightRest) nightRest = value1;
if (value2 > nightRest) nightRest = value2;
}
if (DayRangeStart >= sDate && DayRangeEnd > eDate)
{
TimeSpan value1 = (nextPostSdate - eDate);
if (value1 > nightRest) nightRest = value1;
}
if (DayRangeStart < sDate && DayRangeEnd <= eDate)
{
TimeSpan value1 = (sDate - prevPostSdate);
if (value1 > nightRest) nightRest = value1;
}
if (DayRangeStart >= sDate && DayRangeEnd <= eDate)
{
nightRest = TimeSpan.FromHours(0);
}
}
else
{
nightRest = TimeSpan.FromHours(24 * dayRange);
}
i++;
}
}
return nightRest;
}
e.g. 70,105 - calculate any date of birth that meets the age range of the parameters
CalculateDob(int youngestAge, int oldestAge)
{
Random r = new Random();
int age = 0;
age = r.Next(70, 105);
var year = DateTime.Now.Year - age;
var month = r.Next(1, 12);
var day = r.Next(1, 28);
return new DateTime(year, month, day);
}
My current soluton almost works, but fails in some edge cases i.e. returns 69 in some circumstances due what i presume a month issue.
Any suggestions?
Reason you are getting 69 years of age sometimes is because if CalculateDob returns month which is after current month (DateTime.Now); then Dob still wouldn't reach 70 years. Also, you should bring random constructor out from the method, and make it static so that you don't keep seeding rand generator during every call.
public static void Main(string[] args)
{
for (int i = 0; i < 100; i++)
{
var birthDate = CalculateDob(70, 105);
var now = DateTime.Now;
int age = now.Year - birthDate.Year;
if (now.Month < birthDate.Month || (now.Month == birthDate.Month && now.Day < birthDate.Day))
{
age--;
}
Console.WriteLine(age);
}
}
//Construct this as static so that we don't keep seeding the rand
private static readonly Random _random = new Random();
static DateTime CalculateDob(int youngestAge, int oldestAge)
{
int age = 0;
age = _random.Next(70, 105);
var today = DateTime.Now;
var year = today.Year - age;
var month = _random.Next(1, 12);
//Age might less than youngest age,
//if born at/after current month, edge condition
if (month >= today.Month)
{
month = today.Month - 1;
if (month < 1)
{
year--;
month = 12;
}
}
var day = _random.Next(1, DateTime.DaysInMonth(year, month));
return new DateTime(year, month, day);
}
Instead of specifying
"string[] week_Days = {"Monday", "Tuesday"...};"
How to dynamically set days as array?
I tried solving it, but couldn't get starting day as "Sunday" -
DateTime days = DateTime.Now;
string[] weekDays = new string[7];
for (int i = 0; i < weekDays.Length; i++)
{
weekDays[i] = string.Format("{0:dddd}", days.AddDays(i));
Console.WriteLine(weekDays[i]);
days = DateTime.Now;
}
Output -
Wednesday
Thursday
Friday
Saturday
Sunday
Monday
Tuesday
using System;
using System.Globalization;
static void Main()
{
string[] weekDays = new CultureInfo("en-us").DateTimeFormat.DayNames;
for (int i = 1; i <= 7; i++)
Console.WriteLine(weekDays[i % 7]);
}
You could use the DayOfWeek enum:
string[] weekdays = Enum.GetNames(typeof(DayOfWeek));
EDIT: If you need to change the start day of the week:
private static string[] GetWeekdays(DayOfWeek firstDayOfWeek)
{
string[] weekdays = new string[7];
DateTime dateTime = DateTime.Now;
while (dateTime.DayOfWeek != firstDayOfWeek)
{
// Find the next date with start day of week
dateTime = dateTime.AddDays(1);
}
for (int i = 0; i < 7; i++)
{
// Get day of week of current day, add 1 day, iterate 7 times.
weekdays[i] = dateTime.DayOfWeek.ToString();
dateTime = dateTime.AddDays(1);
}
return weekdays;
}
You can use this LINQ query. The only challenge is to get the correct order:
DayOfWeek firstWeekDay = DateTimeFormatInfo.CurrentInfo.FirstDayOfWeek;
string[] weekDays = Enum.GetValues(typeof(DayOfWeek)).Cast<DayOfWeek>()
.OrderBy(dow => dow < firstWeekDay ? 7-(int)dow : dow-firstWeekDay)
.Select(dow => dow.ToString())
.ToArray();
Maybe someone has a more elegant way than this OrderBy.
string[] week_Days = new string[7];
DayOfWeek day = DayOfWeek.Sunday;
for (int i = 0; i < week_Days.Length; i++)
{
week_Days[i] = day.ToString();
Console.WriteLine(week_Days[i]);
day++;
}
Instead of assigning DateTime property, Use DayOfWeek. it solves the issue with ease.
Your code fails because DateTime.Now isn't always on a sunday:
DateTime days = DateTime.Now;
Better use the existing DayOfWeek enum:
string[] weekdays = Enum.GetNames(typeof(DayOfWeek));
This could be done in this way too. Until you get "Monday", add days to the DateTime.Now.
int j = 0;
string[] weekDays = new string[7];
DateTime days = DateTime.Now;
while (weekDays[0] != "Sunday")
{
days = days.AddDays(j++);
weekDays[0] = string.Format("{0:dddd}", days);
if (weekDays[0] != "Sunday")
days = DateTime.Now;
}
for (int i = 0; i < weekDays.Length; i++)
{
weekDays[i] = string.Format("{0:dddd}", days.AddDays(i));
Console.WriteLine(weekDays[i]);
}
How can I count the number of holidays from database between 2 days
I already count the number of business days but how can i minus to week days the number of holidays counted between 2 days.
Assuming that I have a Holidays table with
Halloween 11-01
Christmas 12-25
Dates:
start date = 10-25
end date = 01-30
Answer should be: Business days - Holiday Number between 2 dates ;
Here's my code:
public static void GetBusinessDays(DateTime startD, DateTime endD)
{
double calcBusinessDays =
1 + ((endD - startD).TotalDays * 5 -
(startD.DayOfWeek - endD.DayOfWeek) * 2) / 7;
if (endD.DayOfWeek == DayOfWeek.Saturday) calcBusinessDays--;
if (startD.DayOfWeek == DayOfWeek.Sunday) calcBusinessDays--;
MessageBox.Show(" " + calcBusinessDays);
}
from this link :Calculate the number of business days between two dates?
Button:
private void button1_Click(object sender, EventArgs e)
{
GetBusinessDays(Convert.ToDateTime(metroDateTime1.Value.ToString("yyyy-MM-dd")), Convert.ToDateTime(metroDateTime2.Value.ToString("yyyy-MM-dd")));
}
PS: I'm new to c#
While only
while (Start.Date <= End.Date)
{
if (Start.DayOfWeek == DayOfWeek.Saturday || Start.DayOfWeek == DayOfWeek.Sunday)
holidays++;
Start = Start.AddDays(1);
}
Could calculate the holiday count, but the code below is more efficient, as we already know that every 7 days has 2 holiday in it and don't have to go through it and only checking days % 7 (6 days at most) for holidays is enough.
int days = (int)(End - Start).TotalDays + 1;
int holidays = days / 7 * 2;
int remain = days % 7;
DateTime dt = End.AddDays(-remain);
while (dt.Date <= End.Date)
{
if (dt.DayOfWeek == DayOfWeek.Saturday || dt.DayOfWeek == DayOfWeek.Sunday)
holidays++;
dt = dt.AddDays(1);
}
int year = Start.Year;
do
{
dt = new DateTime(year, 12, 25); //is chritsmass right?
if (dt >= Start && dt <= End && dt.DayOfWeek!=DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday) holidays++;
dt = new DateTime(year, 7, 4); // 4th of july
if (dt >= Start && dt <= End && dt.DayOfWeek!=DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday) holidays++;
dt = new DateTime(year, 10, 31); // holoween
if (dt >= Start && dt <= End && dt.DayOfWeek!=DayOfWeek.Saturday && dt.DayOfWeek != DayOfWeek.Sunday) holidays++;
year++;
} while (year <= End.Year);
int businessDays = days - holidays;
Live Demo
I need to calculate the number of days between two dates (DateTime) but with a twist. I want to know how many days fall into each of the months that the two days span. Is there an easy way two do it?
Example:
I have start date 30/03/2011 and end date 05/04/2011 then the result should be something like:
var result = new Dictionary<DateTime, int>
{
{ new DateTime(2011, 3, 1), 2 },
{ new DateTime(2011, 4, 1), 5 }
};
You could try something like this:
using System;
using System.Collections.Generic;
static class Program {
// return dictionary tuple<year,month> -> number of days
static Dictionary<Tuple<int, int>, int> GetNumberOfDays(DateTime start, DateTime end) {
// assumes end > start
Dictionary<Tuple<int, int>, int> ret = new Dictionary<Tuple<int, int>, int>();
DateTime date = end;
while (date > start) {
if (date.Year == start.Year && date.Month == start.Month) {
ret.Add(
Tuple.Create<int, int>(date.Year, date.Month),
(date - start).Days + 1);
break;
} else {
ret.Add(
Tuple.Create<int, int>(date.Year, date.Month),
date.Day);
date = new DateTime(date.Year, date.Month, 1).AddDays(-1);
}
}
return ret;
}
static void Main(params string[] args) {
var days = GetNumberOfDays(new DateTime(2011, 3, 1), new DateTime(2011, 4, 1));
foreach (var m in days.Keys) {
Console.WriteLine("{0}/{1} : {2} days", m.Item1, m.Item2, days[m]);
}
}
}
You can use the class Month of the Time Period Library for .NET:
// ----------------------------------------------------------------------
public Dictionary<DateTime,int> CountMonthDays( DateTime start, DateTime end )
{
Dictionary<DateTime,int> monthDays = new Dictionary<DateTime, int>();
Month startMonth = new Month( start );
Month endMonth = new Month( end );
if ( startMonth.Equals( endMonth ) )
{
monthDays.Add( startMonth.Start, end.Subtract( start ).Days );
return monthDays;
}
Month month = startMonth;
while ( month.Start < endMonth.End )
{
if ( month.Equals( startMonth ) )
{
monthDays.Add( month.Start, month.DaysInMonth - start.Day + 1 );
}
else if ( month.Equals( endMonth ) )
{
monthDays.Add( month.Start, end.Day );
}
else
{
monthDays.Add( month.Start, month.DaysInMonth );
}
month = month.GetNextMonth();
}
return monthDays;
} // CountMonthDays
Usage:
// ----------------------------------------------------------------------
public void CountDaysByMonthSample()
{
DateTime start = new DateTime( 2011, 3, 30 );
DateTime end = new DateTime( 2011, 4, 5 );
Dictionary<DateTime, int> monthDays = CountMonthDays( start, end );
foreach ( KeyValuePair<DateTime, int> monthDay in monthDays )
{
Console.WriteLine( "month {0:d}, days {1}", monthDay.Key, monthDay.Value );
}
// > month 01.03.2011, days 2
// > month 01.04.2011, days 5
} // CountDaysByMonthSample
Simple yes, fast no:
DateTime StartDate = new DateTime(2011, 3, 30);
DateTime EndDate = new DateTime(2011, 4, 5);
int[] DaysPerMonth = new int[12];
while (EndDate > StartDate)
{
DaysPerMonth[StartDate.Month]++;
StartDate = StartDate.AddDays(1);
}
Here's my solution. I did a quick check and it seems to work... let me know if there are any problems:
public Dictionary<DateTime, int> GetMontsBetween(DateTime startDate, DateTime EndDate)
{
Dictionary<DateTime, int> rtnValues = new Dictionary<DateTime, int>();
DateTime startMonth = new DateTime(startDate.Year, startDate.Month, 1);
DateTime endMonth = new DateTime(EndDate.Year, EndDate.Month, 1);
//some checking
if (startDate >= EndDate)
{
rtnValues.Add(startMonth, 0); // Or return null;
}
else if (startDate.Month == EndDate.Month && startDate.Year == EndDate.Year)
{
rtnValues.Add(startMonth, EndDate.Day - startDate.Day);
}
else
{
//Add first month remaining days
rtnValues.Add(startMonth, DateTime.DaysInMonth(startDate.Year, startDate.Month) - startDate.Day);
//Add All months days inbetween
for (DateTime st = startMonth.AddMonths(1); st < endMonth; st = st.AddMonths(1))
{
rtnValues.Add(new DateTime(st.Year, st.Month, 1), DateTime.DaysInMonth(st.Year, st.Month) );
}
//Add last month days
rtnValues.Add(new DateTime(EndDate.Year, EndDate.Month, 1), EndDate.Day);
}
return rtnValues;
}
Little example of how we can accurately get the total months and days between 2 dates using the built-in DateTime.DaysInMonth method which gives us the number of days in each month so we can get 100% accuracy.
DateTime date1 = DateTime.Now.AddDays(60);
DateTime date2 = DateTime.Now;
TimeSpan ts = date1 - date2;
int totalDays = int.Parse(ts.TotalDays.ToString("0"));
int totalMonths = Math.Abs((date1.Month - date2.Month) + 12 * (date1.Year - date2.Year));
int months = 0;
int days = 0;
int totalDaysInMonths = 0;
for (int i = totalMonths; i > 0; i--)
{
int month = date2.Month + i;
int year = date1.Year;
if (month > 12)
{
year++;
int newMonth = month - 12;
month = newMonth;
}
totalDaysInMonths = totalDaysInMonths + DateTime.DaysInMonth(year, month);
}
if (totalDays > totalDaysInMonths)
{
months = totalMonths - 1;
days = totalDays - totalDaysInMonths;
}
else if (totalDays < totalDaysInMonths)
{
months = totalMonths - 1;
int tempTotalDaysInMonths = 0;
for (int i = months; i > 0; i--)
{
int month = date2.Month + i;
int year = date1.Year;
if (month > 12)
{
year++;
int newMonth = month - 12;
month = newMonth;
}
tempTotalDaysInMonths = tempTotalDaysInMonths + DateTime.DaysInMonth(year, month);
}
days = totalDays - tempTotalDaysInMonths;
}
else
{
months = totalMonths;
}
return string.Format("{0} months and {1} days", months, days);
A very quick and dirty run at it using linqpad:
DateTime start = DateTime.Parse("03/30/2011");
DateTime end = new DateTime(2011,04,05,23,59,59);
var startNextMonthFirstDay = new DateTime(start.Year, start.Month+1, 1);
var diffForStartMonth = (startNextMonthFirstDay - start);
var totalDiff = (end-start);
var diff = Math.Round(totalDiff.TotalDays);
var diffForEndMonth = diff - diffForStartMonth.Days;
Dictionary<DateTime, int> result = new Dictionary<DateTime, int>();
result.Add(new DateTime(start.Year, start.Month, 1), diffForStartMonth.Days);
result.Add(new DateTime(end.Year, end.Month, 1), (int)diffForEndMonth);
//Dictionary<DateTime,int>{{new DateTime(2011,3,1),2},{new DateTime(2011,4,1),5}}
result.Dump();
DateTime dt1 = new DateTime(2011, 12, 12);
DateTime dt2 = new DateTime(2011, 06, 12);
TimeSpan ts = dt1.Subtract(dt2);
String s = ts.Days.ToString();
MessageBox.Show(s);