How Can I Only Work Hours DateTime and Time Addition

1.1k views Asked by At

My code below.I want to do user select 10.06.2015 09:00 - 12.06.2015 13:00 after I will show 2 days 2 hours.

But I want to do Working days and Working Hours beetween 09:00 - 18:00 well users when you 10.06.2015 09:00 - 12.06.2015 13:00 I want to show only 2,5 days.

How can I do?

DateTime t1 = dateTimePicker1.Value.Date;
DateTime t2 = dateTimePicker2.Value.Date;

string s1 = textBox9.Text;
string s2 = textBox10.Text;

DateTime dt1 = t1.AddMinutes(DateTime.Parse(s1).TimeOfDay.TotalMinutes);
DateTime dt2 = t2.AddMinutes(DateTime.Parse(s2).TimeOfDay.TotalMinutes);

var fark = dt2 - dt1;

    label1.Text =
String.Format("{0}{1}{2}",       
fark.Days > 0 ? string.Format("{0} gün", fark.Days) : "",
fark.Hours > 0 ? string.Format("{0} saat ", fark.Hours) : "",
fark.Minutes > 0 ? string.Format("{0} dakika ", fark.Minutes) : "").Trim();
4

There are 4 answers

1
Matt On

Well you can assume that any days in the range, except the first and last are full working days. So you need (AllDaysInRange -2) + HoursInfirstDay + HoursInLastDay.

TimeSpan ts = t2 - t1;
ts.Days = ts.Days - 2; //Allow for the 2 end days

int Day1Hours = t1.Hours - 9;//This removes any hours between 00.00 and 09.00
if (day1Hours > 9) //Then the user was working past 18.00
    ts.Days = ts.Days+1
else
    ts.Hours = ts.Hours + day1Hours;

int Day2Hours = t2.Hours - 9;//This removes any hours between 00.00 and 09.00
if (day2Hours > 9) //Then the user was working past 18.00
    ts.Days = ts.Days+1
else
    ts.Hours = ts.Hours + day2Hours;

If you can get this to work (I have written it from memory), then I'd wrap the code to convert the hours of the end days into a method rather than repeating it.

1
Krisztián Kis On

According to DateTime Picker In WinForm How To Pick Time? post, you can change your DateTimePicker, to function with times as well.

To limit the range which the users can select, you can modify your ValueChanged event or write your own validation for it. Probably the simplest is:

private void dateTimePicker1_ValueChanged(object sender, EventArgs e)
    {
        if (dateTimePicker1.Value.Hour < 10) // the 10 is just a random number, you can change it to your own limit
            dateTimePicker1.Value = this.dateTimePicker1.Value.AddHours(10 - dateTimePicker1.Value.Hour);
    }

To calculate your 2,5 day according to the working hours, i would write a function to handle this responsibility like:

private int TimeInWorkingHours(DateTime start, DateTime end, int firstHour, int lastHour)
    {
        int days = Math.Min(end.Subtract(start).Days - 2, 0) ;
        int hoursInADay = lastHour - firstHour;

        int result = days * hoursInADay;

        result += start.Hour - firstHour;
        result += lastHour - end.Hour;

        return result;
    }

This way you call TimeInWorkingHours(...) function with your start date and end date, also providing your first and last working hours.

First you calculate the worked days, than add the border hours. This way you get your worked hours which you can then divide by the work hours to get your working days.

0
Stephen On

try this

private void GetProperOfficeHours(ref DateTime date)
    {
        int minHour = 9, maxHour = 17;

        if (date.Hour < minHour) //if earlier than office hours - start from 9am
        {
            date = date + new TimeSpan(9, 0, 0);
        }
        else if (date.Hour > maxHour) //if later than office hours - go to next day 9am
        {
            date = date.AddDays(1) + new TimeSpan(9, 0, 0);
        }
    }

Then ...

//assuming firstDate & lastDate have date and time
        int[] weekendDays = new int[2] { 0, 6 }; // Sunday and Saturday

        GetProperOfficeHours(ref firstDate);
        GetProperOfficeHours(ref lastDate);

        while (weekendDays.Contains((int)firstDate.DayOfWeek))
        {
            //get next date
            firstDate = firstDate.AddDays(1);
        }

        while (weekendDays.Contains((int)lastDate.DayOfWeek))
        {
            //get prev date
            lastDate = lastDate.AddDays(-1);
        }

        double hourDiff = Math.Abs(firstDate.Hour - lastDate.Hour) / 8.0; //8 office hours
        double dayDifference = 0;
        while (firstDate.Date <= lastDate.Date) //Loop and skip weekends
        {
            if (!weekendDays.Contains((int)firstDate.DayOfWeek)) //can also check for holidays here
                dayDifference++;

            firstDate = firstDate.AddDays(1);
        }

        dayDifference = dayDifference + hourDiff;

May need some tweaking, hope you find it helpful.

1
Leonardo On

Try this

bool IsWorkingDay(DateTime dt)
{
    int year = dt.Year;
    Dictionary<DateTime, object> holidays = new Dictionary<DateTime, object>();
    holidays.Add(new DateTime(year, 1, 1), null);
    holidays.Add(new DateTime(year, 1, 6), null);
    holidays.Add(new DateTime(year, 4, 25), null);
    holidays.Add(new DateTime(year, 5, 1), null);
    holidays.Add(new DateTime(year, 6, 2), null);
    holidays.Add(new DateTime(year, 8, 15), null);
    holidays.Add(new DateTime(year, 11, 1), null);
    holidays.Add(new DateTime(year, 12, 8), null);
    holidays.Add(new DateTime(year, 12, 25), null);
    holidays.Add(new DateTime(year, 12, 26), null);
    DateTime easterMonday = EasterSunday(year).AddDays(1);
    if (!holidays.ContainsKey(easterMonday))
        holidays.Add(easterMonday, null);

    if (!holidays.ContainsKey(dt.Date))
        if (dt.DayOfWeek > DayOfWeek.Sunday && dt.DayOfWeek < DayOfWeek.Saturday)
            return true;

    return false;
}

string WorkingTime(DateTime dt1, DateTime dt2)
{
    // Adjust begin datetime
    if (IsWorkingDay(dt1))
    {
        if (dt1.TimeOfDay < TimeSpan.FromHours(9))
            dt1 = new DateTime(dt1.Year, dt1.Month, dt1.Day, 9, 0, 0);
        else if (dt1.TimeOfDay > TimeSpan.FromHours(13) && dt1.TimeOfDay < TimeSpan.FromHours(14))
            dt1 = new DateTime(dt1.Year, dt1.Month, dt1.Day, 14, 0, 0);
        else if (dt1.TimeOfDay > TimeSpan.FromHours(18))
            dt1 = new DateTime(dt1.Year, dt1.Month, dt1.Day, 9, 0, 0).AddDays(1);
    }
    else
        dt1 = new DateTime(dt1.Year, dt1.Month, dt1.Day, 9, 0, 0).AddDays(1);

    // Adjust end datetime
    if (IsWorkingDay(dt2))
    {
        if (dt2.TimeOfDay < TimeSpan.FromHours(9))
            dt2 = new DateTime(dt2.Year, dt2.Month, dt2.Day, 18, 0, 0).AddDays(-1);
        else if (dt2.TimeOfDay > TimeSpan.FromHours(18))
            dt2 = new DateTime(dt2.Year, dt2.Month, dt2.Day, 18, 0, 0);
        else if (dt2.TimeOfDay > TimeSpan.FromHours(13) && dt2.TimeOfDay < TimeSpan.FromHours(14))
            dt2 = new DateTime(dt2.Year, dt2.Month, dt2.Day, 13, 0, 0);
    }
    else
        dt2 = new DateTime(dt2.Year, dt2.Month, dt2.Day, 18, 0, 0).AddDays(-1);

    double days = 0;
    double hours = 0;
    double minutes = 0;

    if (dt2 > dt1)
    {
        // Move dt1 forward to reach dt2 day chacking for working days
        while (dt1.DayOfYear < dt2.DayOfYear)
        {
            if (IsWorkingDay(dt1))
                days++;
            dt1 = dt1.AddDays(1);
        }

        // Now get the worked hours as if were on the same day in the same manner
        TimeSpan sdwt = dt2 - dt1;
        if (dt1.TimeOfDay < TimeSpan.FromHours(13) && dt2.TimeOfDay > TimeSpan.FromHours(14))
            sdwt -= TimeSpan.FromHours(1);
        if (sdwt == TimeSpan.FromHours(8))
            days++;
        else
        {
            hours = sdwt.Hours;
            minutes = sdwt.Minutes;
        }
    }

    // There is a pause in between so adjust if the interval include it

    var totalminutes = (days * 8 * 60 + hours * 60 + minutes);

    string res = String.Format("{0} days {1} hours {2} minutes",
        days,
        hours,
        minutes);
    string totRes = String.Format("{0} days {1} hours {2} minutes",
        totalminutes / 8 / 60,
        totalminutes / 8,
        totalminutes);

    return res + "\r\n" + totRes;
}