开发者

How to round-off hours based on Minutes(hours+0 if min<30, hours+1 otherwise)?

I need to round-off the hours based on the minutes in a DateTime variable. The condition is: if minutes are less than 30, then minutes must be set to zero and no changes to hours, else if minutes >=30, then hours must be set to hours+1 and minutes are again set to zero. Seconds are ignored.

example:

11/08/2008 04:30:49 should become 11/08/2008 05:00:00

and 11/08/2008 04:29:49 should become 11/08/2008 04:00:00

I have written code which works perfectly fine, but just wanted to know a better method if could be written and a开发者_开发问答lso would appreciate alternative method(s).

string date1 = "11/08/2008 04:30:49";
DateTime startTime;
DateTime.TryParseExact(date1, "MM/dd/yyyy HH:mm:ss", null,     
    System.Globalization.DateTimeStyles.None, out startTime);

if (Convert.ToInt32((startTime.Minute.ToString())) > 29)
{
    startTime = DateTime.Parse(string.Format("{0}/{1}/{2} {3}:{4}:{5}",
        startTime.Month.ToString(), startTime.Day.ToString(), 
        startTime.Year.ToString(), startTime.Hour.ToString(), "00", "00"));
    startTime = startTime.Add(TimeSpan.Parse("01:00:00"));
    Console.WriteLine("startTime is :: {0}", 
        startTime.ToString("MM/dd/yyyy HH:mm:ss"));
}
else
{
    startTime = DateTime.Parse(string.Format("{0}/{1}/{2} {3}:{4}:{5}", 
        startTime.Month.ToString(), 
        startTime.Day.ToString(), startTime.Year.ToString(), 
        startTime.Hour.ToString(), "00", "00"));

        Console.WriteLine("startTime is :: {0}", 
        startTime.ToString("MM/dd/yyyy HH:mm:ss"));
}


Just as an alternative:

public static DateTime Round( DateTime dateTime )
{
    var updated = dateTime.AddMinutes( 30 );
    return new DateTime( updated.Year, updated.Month, updated.Day,
                         updated.Hour,  0, 0, dateTime.Kind );
}


If speed is an issue, the following should be the fastest way:

static DateTime RoundToHour(DateTime dt){
    long ticks = dt.Ticks + 18000000000;
    return new DateTime(ticks - ticks % 36000000000, dt.Kind);
}

It's also a pretty straight-forward and simple way to do it.

To explain, a DateTime structure doesn't actually have fields that store the year, month, day, hour, minute, etc. It stores one single long value, the number of "ticks" since a certain epoch (Jan 1, 1 AD). A tick is 100 nanoseconds, or one 10,000,000th of a second.

Any time you use any of the date/time properties, it divides by the proper constant.

So here, we add a constant equal to 30 minutes (30 * 60 * 1e7 = 18000000000 ticks), then subtract the remainder after dividing by a constant equal to one hour (60 * 60 * 1e7 = 36000000000 ticks).


What about:

public static DateTime RoundToHours(DateTime input)
{
DateTime dt = new DateTime(input.Year, input.Month, input.Day, input.Hour, 0, 0);

    if (input.Minute > 29)
      return dt.AddHours(1);
    else
      return dt;
}

No need to convert to string and back again!

EDIT:
Using a input.Hour+1 in the constructor will fail if the Hour is 23. The .AddHours(1) will correctly result in '0:00' the next day.


Here goes!

var rounded = date.AddMinutes(30).Date.AddHours(date.AddMinutes(30).Hour);

And for those that want it floored

var floored = date.Date.AddHours(date.Hours)


  DateTime s = DateTime.Now;
  if (s.Minute > 30) s = s.AddHours(1); //only add hours if > 30
  if (s.Minute == 30 && s.Second > 0) s = s.AddHours(1); //add precision as needed
  s = new DateTime(s.Year, s.Month, s.Day, s.Hour, 0, 0);


Extending Hans Kestings good Answer:

public DateTime RoundToHours(DateTime input)
{
      DateTime dt = new DateTime(input.Year, input.Month, input.Day, input.Hour, 0, 0);
      return dt.AddHours((int)(input.Minutes / 30));
}

The (int) Cast might not be required.

EDIT: Adapted the corrections Hans Kesting made in his Answer.


To improve upon some of the other methods, here is a method that will also preserve the DateTime Kind:

/// <summary>
/// Rounds a DateTime to the nearest hour.
/// </summary>
/// <param name="dateTime">DateTime to Round</param>
/// <returns>DateTime rounded to nearest hour</returns>
public static DateTime RoundToNearestHour(this DateTime dateTime)
{
  dateTime += TimeSpan.FromMinutes(30);

  return new DateTime(dateTime.Year, dateTime.Month, dateTime.Day, dateTime.Hour, 0, 0, dateTime.Kind);
}


Based on P Daddy's solution, I propose to not hardcode that big number of ticks to one hour. Hardcoding is evil, isn't it? With this modified solution, you can now round any given time to any number of minutes:

    public DateTime RoundToMinutes(DateTime dt, int NrMinutes)
    {
        long TicksInNrMinutes = (long)NrMinutes * 60 * 10000000;//1 tick per 100 nanosecond
        long ticks = dt.Ticks + TicksInNrMinutes / 2;
        return new DateTime(ticks - ticks % TicksInNrMinutes, dt.Kind);
    }

I use this for rounding to the nearest 5 minutes, e.g. 22:23 becomes 22:25.

Years ago I used the same method to round amounts of money to the nearest 25 cent, e.g. $ 22.23 becomes $ 22.25. But the project manager sometimes changed his mind, but changing the rounding to the nearest 10 or 5 cent would be trivial. So now I similarly do not have to get nervous when my project mgr wants rounding times to another round nr of minutes.

So this rounding method is both fast, and flexible.


My method was already found and published in this 2008 SO solution


DateTime dtm = DateTime.Now;
if (dtm.Minute < 30)
{
     dtm = dtm.AddMinutes(dtm.Minute * -1);
}
else
{    
     dtm = dtm.AddMinutes(60 - dtm.Minute);
}
dtm = dtm.AddSeconds(dtm.Second * -1);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜