0

I am attempting to create a timesheet calculator which takes calculates the time an employee works and I am close, with one problem.

As I perform the calculation, I only want hours and minutes to display. I am able to get that done, but that causes an issue. If the employee punches out before a full minute is elapsed, that minute is not included in the calculation.

For example, if an emp punches in at 12:00:30 and punches out at 5:00:29, that last minute is not counted in the calculation, so the time shows as 4:59 instead of 5:00.

How do I get the calculation to be based on the hours and minutes and exclude seconds completely?

This is the code I have:

    private void btnPunchOut_Click(object sender, EventArgs e)
    {

        DateTime stopTime = DateTime.Now;
        lblPunchOutTime.Text = stopTime.ToShortTimeString();
        TimeSpan timeWorked = new TimeSpan();
        timeWorked = stopTime - startTime;                  
        lblTimeWorked.Text = timeWorked.ToString(@"hh\:mm");
    }
abatishchev
  • 98,240
  • 88
  • 296
  • 433
tddyballgm
  • 59
  • 1
  • 9
  • 7
    Your 4 previous questions have collected **12** answers. You might want to consider accepting some of them (as described in the [tour]). Accepting answers and upvoting posts you find useful helps other users find good answers. Its a way you can help others even if you are not in a position to post answers – Ňɏssa Pøngjǣrdenlarp Jan 11 '18 at 00:55
  • 2
    Possible duplicate of [How can I round up the time to the nearest X minutes?](https://stackoverflow.com/questions/7029353/how-can-i-round-up-the-time-to-the-nearest-x-minutes) – Baddack Jan 11 '18 at 01:04

2 Answers2

1

Use TimeSpan.TotalSeconds perhaps...And then add 30 seconds or more, before you convert it to hours by dividing by 3600.

As in

lblTimeWorked.Text = ((timeWorked.TotalSeconds+30)/3600).ToString("0.00") + " hours";

Use Timespan.TotalHours if you want the hours.

But if you want to be accurate, you should create a separate class dedicated to calculating the hours worked by a staff member. Then you can encapsulate lots of business rules in the dedicated class. Staff have entitlements and overtime, expenses or penalty rates - so this can get complex if done properly.

Grantly
  • 2,546
  • 2
  • 21
  • 31
  • See https://en.wikipedia.org/wiki/Rounding for discussion on the various kinds of rounding. @Rantly's scheme rounds up from 0.5 to 1.0 and down for < 0.5. This may in fact be legal in some jurisdictions, but in others, it might not be. In other words, it might always be required to round up to the next full minute. Review your labor laws before writing such code. – jwdonahue Jan 11 '18 at 01:07
  • 1
    It would be easier to subtract the seconds from start and stop, no? (also `CInt` is a VBism) – Ňɏssa Pøngjǣrdenlarp Jan 11 '18 at 01:07
1

If you want a calculation that really ignores the seconds, the clearest way to accomplish that is to get rid of the seconds on both the start time and the end time. It might not seem accurate because it allows a difference of one second to become a difference of one minute. But that could still be a valid business rule, that you want to subtract according the the minutes that appeared on the clock rather than the actual elapsed seconds.

In other words,

1:00:01 is adjusted to 1:00:00.
1:00:59 is adjusted to 1:00:00.
1:01:00 is "adjusted" to 1:01:00.
1:01:01 is adjusted to 1:01:00.

You can accomplish that with an extension like this:

public static class TimespanExtensions
{
    public static TimeSpan TrimToMinutes(this TimeSpan input)
    {
        return TimeSpan.FromMinutes(Math.Truncate(input.TotalMinutes));
    }
}

(I'm sure there's a more efficient way of truncating the seconds, but at least this is clear.)

Now instead of having to figure out how to calculate the difference while rounding seconds or adding seconds, you just trim the seconds before calculating the difference. Here's a unit test:

[TestMethod]
public void NumberOfMinutesIgnoresSeconds()
{
    var startTime = TimeSpan.FromSeconds(59).TrimToMinutes();
    var endTime = TimeSpan.FromSeconds(60).TrimToMinutes();
    Assert.AreEqual(1, (endTime - startTime).TotalMinutes);
}

One Timespan represents 59 seconds, and the next one is 60, or the first second of the next minute. But if you trim the seconds and then calculate the difference you get exactly one minute.

In the context of your code,

DateTime stopTime = DateTime.Now;
lblPunchOutTime.Text = stopTime.ToShortTimeString();
var timeWorked = stopTime.TrimToMinutes() - startTime.TrimToMinutes();                  
lblTimeWorked.Text = timeWorked.ToString(@"hh\:mm");
Scott Hannen
  • 27,588
  • 3
  • 45
  • 62
  • I must be an idiot because I just can't get it to work. To me, the way to do this is to remove the seconds from the very beginning, and that way they would never be part of the equation. But for the life of me, I can't get it to work and I can't seem to figure out how to format DateTime when I create the DateTime variables. – tddyballgm Jan 13 '18 at 03:17
  • My problem is not getting the times or result to display correctly, my problem is trying to get the calculation to work correctly. The seconds are always part of the calculation which causes the issue, and I can't figure out how to remove the seconds so they are not part of the equation. I have tried all the examples above, but nothing seems to work. Confidence is high I am doing something wrong, but I can't figure out what. – tddyballgm Jan 13 '18 at 03:30
  • That's exactly what this is for. Instead of trying to make it work with the seconds, get the time with the seconds completely removed. Then use that time for the calculations. – Scott Hannen Jan 13 '18 at 12:52