views:

313

answers:

7

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 also 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"));
}
+2  A: 
  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);
Russell Steen
You missed the Seconds, they will be left untouched. Better to create 1 new DateTime, copying only the values you need.
Hans Kesting
@Russell, I don't know why but AddHours() method isn't working .. though if condition gets satisfied,
infant programmer
missed an assignment. have to assign the result of addhours back to s. typo
Russell Steen
@Hans, @Russell, I have added `startTime = startTime.AddSeconds(-1 * startTime.Second);` so seconds are set to 0 too, not an issue.
infant programmer
infant - good catch. I changed it to a new datetime for more precision (will also truncate any milliseconds that way)
Russell Steen
+6  A: 

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.

Hans Kesting
ohk .. thanx .. :-) its working.
infant programmer
+1  A: 
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);
Hojo
+1 for the simpler and understandable code :-)
infant programmer
+3  A: 

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.

dbemerlin
thank you very much for reaching the requirement. :-)
infant programmer
+5  A: 

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));
}

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).

P Daddy
wow! [15 chars]
infant programmer
thanks for the explanation. I try to implement this technique else where necessary.
infant programmer
+4  A: 

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 );
}
tvanfosson
You might want to update your answer to include preserving the dateTime Kind- see my post http://stackoverflow.com/questions/2499479/how-to-round-off-hours-based-on-minuteshours0-if-min30-hours1-otherwise/2906684#2906684
CrimsonX
@CrimsonX - updated.
tvanfosson
+1  A: 

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);
}
CrimsonX