10

I have a property in my code where users can enter a timespan in HH:mm like

10:32
10:44
15:45

I want to round off in my property to the nearest 15mins but i dont have datetime here. I only need to do it with Timespan

10:32 to 10:30
10:44 to 10:45
15:45 to 15:45
01:02 to 01:00
02:11 to 02:15
03:22 to 03:15
23:52 to 00:00

Tried all these solutions but they involve Datetime in them

How can I round up the time to the nearest X minutes?
Is there a simple function for rounding a DateTime down to the nearest 30 minutes, in C#?
DotNet Roundoff datetime to last 15 minutes

Community
  • 1
  • 1
s-a-n
  • 767
  • 2
  • 9
  • 27
  • `TimeSpan` is an immutable struct, so you can't change a created instance, but you you can create a new `TimeSpan` from an existing one by extracting its parts. `TimeSpan` exposes a `Minutes` property - extract this value, round to the nearest quarter hour (ensure if you round up to the 60th minute you increase the `Hours`, and if you round up to the 24th hour you increase the `Days`), and create a new instance of `TimeSpan`. – Preston Guillot Jun 03 '14 at 02:43

5 Answers5

19

I think you want something like this:

public static class TimeSpanExtensions
{
    public static TimeSpan RoundToNearestMinutes(this TimeSpan input, int minutes)
    {
        var totalMinutes = (int)(input + new TimeSpan(0, minutes/2, 0)).TotalMinutes;

        return new TimeSpan(0, totalMinutes - totalMinutes % minutes, 0);
    }
}

If you pass in 15 as your chosen interval for rounding, the function will first add 7 mins, then round down to the nearest 15 mins. This should give you what you want.

Because the above is written an extension method, you can use it like this:

var span1 = new TimeSpan(0, 10, 37, 00).RoundToNearestMinutes(15);
var span2 = new TimeSpan(0, 10, 38, 00).RoundToNearestMinutes(15);

The first one becomes 10:30, and the second one becomes 10:45 (as desired).

Baldrick
  • 11,712
  • 2
  • 31
  • 35
  • should it not be return new Timespan(input.Hours, minutes - minutes % 15, input.Seconds)?? – justanotherdev Jun 03 '14 at 03:13
  • @softwaregeek: No, because I've already converted to `TotalMinutes` previously. – Baldrick Jun 03 '14 at 03:14
  • 3
    A simpler algorithm would be: `return new TimeSpan(0, Math.Round(input.TotalMinutes / minutes) * minutes, 0);` – Daniel Jun 03 '14 at 04:26
  • But when I input 23:52 it is supposed to be 00:00 but Im getting 23:45. Can there be a work around for tht please? – s-a-n Jun 03 '14 at 04:34
  • @s-a-n: 23:52 is actually nearer to 23:45 than it is to 24:00... Even so, it seems like you want a TimeSpan to behave like a DateTime. The workaround in this case would be to subtract 24 hours from it if it's >= 24 hours. – Baldrick Jun 03 '14 at 04:40
  • @DanielCook: I like it. Depending on OP's comments, I might update to include your method. Thanks! – Baldrick Jun 03 '14 at 04:41
  • @Baldrick Sure but my requirement is when it is 00:52, I will add the code to alter the hours accordingly but the minutes must be rounded off to 00 again rather than 45. – s-a-n Jun 03 '14 at 04:50
  • 2
    @s-a-n: What's the rule here? 52 is *7 mins* away from 45, but *8 mins away* from 00. So why are you rounding up? Also, in your requirements, can you explain why you are rounding 03:22 to 03:25? 03:25 isn't a multiple of 15 minutes. I think you might need to update your question to clarify these points. – Baldrick Jun 03 '14 at 04:56
4

I liked Baldrick's answer but I discovered that it does not work when using negative TimeSpan values (as in the case of time zone offsets).

I amended his original code as follows and this seems to work for both positive and negative TimeSpan values.

public static class TimeSpanExtensions
{
    public static TimeSpan RoundToNearestMinutes(this TimeSpan input, int minutes)
    {
        var halfRange = new TimeSpan(0, minutes/2, 0);
        if (input.Ticks < 0)
            halfRange = halfRange.Negate();
        var totalMinutes = (int)(input + halfRange).TotalMinutes;
        return new TimeSpan(0, totalMinutes - totalMinutes % minutes, 0);
    }
}
Larry C
  • 41
  • 1
2

I know this is rather late, but I thought it might be useful for anyone looking for an answer, as I was when I found this question. Note that this can be used to round up to units of any length of time, and can be easily modified to round down or round to the nearest block of time (by changing Math.Ceiling to Math.Floor or Math.Round)

public TimeSpan RoundTimeSpanUp(TimeSpan span, TimeSpan roundingTimeSpan)
{
    long originalTicks = roundingTimeSpan.Ticks;
    long roundedTicks = (long)(Math.Ceiling((double)span.Ticks / originalTicks) * originalTicks);
    TimeSpan result = new TimeSpan(roundedTicks);
    return result;
}

It can be used like this:

TimeSpan roundedMinutes = RoundTimeSpanUp(span, TimeSpan.FromMinutes(15));

or to round up by any unit of time, like this:

TimeSpan roundedHours = RoundTimeSpanUp(span, TimeSpan.FromHours(1));

1

I realize this is quite late, however the answer provided by @Baldrick inspired my own solution:

public static class TimeSpanExtensions
{
    /// <summary>
    /// Rounds a TimeSpan based on the provided values.
    /// </summary>
    /// <param name="ts">The extension target.</param>
    /// <param name="Direction">The direction in which to round.</param>
    /// <param name="MinutePrecision">The precision to round to.</param>
    /// <returns>A new TimeSpan based on the provided values.</returns>
    public static System.TimeSpan Round(this System.TimeSpan ts, 
        RoundingDirection Direction, 
        int MinutePrecision)
    {
        if(Direction == RoundingDirection.Up)
        {
            return System.TimeSpan.FromMinutes(
                MinutePrecision * Math.Ceiling(ts.TotalMinutes / MinutePrecision)); 
        }

        if(Direction == RoundingDirection.Down)
        {
            return System.TimeSpan.FromMinutes(
                MinutePrecision * Math.Floor(ts.TotalMinutes / MinutePrecision)); 
        }

        // Really shouldn't be able to get here...
        return ts; 
    }
}

/// <summary>
/// Rounding direction used in rounding operations. 
/// </summary>
public enum RoundingDirection
{
    /// <summary>
    /// Round up.
    /// </summary>
    Up, 
    /// <summary>
    /// Round down.
    /// </summary>
    Down
}
SeanH
  • 584
  • 3
  • 18
0

I put together the following logic in code-behind for an old asp forms app to take in a dateTime string from a hidden field in the UI and calculate the variance between client and Server.

 private TimeSpan calculateVariance(string dateString)
        {
            DateTime clientHour = Convert.ToDateTime(dateString);
            DateTime serverHour = DateTime.Now.ToLocalTime();
            TimeSpan timeVariance = clientHour - serverHour;

            TimeSpan roundedTimeVariance;

            //ROUND VARIANCE TO THE NEAREST 15 minutes AND RETURN AS EITHER A POSITIVE OR NEGATIVE TimeSpan Object
            if (timeVariance.Minutes > 52)
            {
                roundedTimeVariance = new TimeSpan(timeVariance.Hours + 1, 0, 0);
            }
            else if (timeVariance.Minutes > 37)
            {
                roundedTimeVariance = new TimeSpan(timeVariance.Hours, 45, 0);
            }
            else if (timeVariance.Minutes > 22)
            {
                roundedTimeVariance = new TimeSpan(timeVariance.Hours, 30, 0 );
            }
            else if (timeVariance.Minutes > 7)
            {
                roundedTimeVariance = new TimeSpan(timeVariance.Hours, 15, 0);
            }
            else
            {
                roundedTimeVariance = new TimeSpan(timeVariance.Hours, 0, 0);
            }

            return roundedTimeVariance;
        }