DateTime.Round extension method
I recently needed to group by minutes (or seconds, or hours, or days) in a LINQ expression, and I found that there isn’t a round function built into the C# DateTime object. The following will round to the nearest second, minute, hour, or day. I stopped there because different months have different numbers of days (and I don’t need to group by months…) but it is easy enough to add months and years to the code.
Keep in mind that this doesn’t give you the current minute (or whatever) rather it gives you the closest minute (or whatever).
using System;
namespace MikeInMadison
{
public static class Extensions
{
public static DateTime Round(this DateTime d, RoundTo rt)
{
DateTime dtRounded = new DateTime();
switch (rt)
{
case RoundTo.Second:
dtRounded = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second);
if (d.Millisecond >= 500) dtRounded = dtRounded.AddSeconds(1);
break;
case RoundTo.Minute:
dtRounded = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, 0);
if (d.Second >= 30) dtRounded = dtRounded.AddMinutes(1);
break;
case RoundTo.Hour:
dtRounded = new DateTime(d.Year, d.Month, d.Day, d.Hour, 0, 0);
if (d.Minute >= 30) dtRounded = dtRounded.AddHours(1);
break;
case RoundTo.Day:
dtRounded = new DateTime(d.Year, d.Month, d.Day, 0, 0, 0);
if (d.Hour >= 12) dtRounded = dtRounded.AddDays(1);
break;
}
return dtRounded;
}
public enum RoundTo
{
Second, Minute, Hour, Day
}
}
}
I’m using this extension method within LINQ to group RateItems by minutes. I’m getting the high, low, open, close, and begin time in a list of stock prices.
var rates = from ri in RateItems
group ri by ri.Time.Round(Extensions.RoundTo.Hour) into g
select new
{
Time = g.First().Time.Round(Extensions.RoundTo.Hour),
High = g.Max(g2 => g2.Bid),
Low = g.Min(g2 => g2.Bid),
Open = g.First().Bid,
Close = g.Last().Bid
};
Thank you for sharing this, this DateTime round functionality was exactly what I needed for something I’m working on.
Evan
Evan
April 21, 2010
Here’s the code in VB.net:
Public Class Extensions
Public Function Round(ByVal d As DateTime, _
ByVal rt As RoundTo) As Date
Dim dtRounded As Date
Select (rt)
Case RoundTo.Second
dtRounded = New DateTime(d.Year, _
d.Month, d.Day, d.Hour, _
d.Minute, d.Second)
If (d.Millisecond >= 500) Then _
dtRounded = dtRounded.AddSeconds(1)
Case RoundTo.Minute
dtRounded = New DateTime(d.Year, _
d.Month, d.Day, d.Hour, _
d.Minute, 0)
If (d.Second >= 30) Then _
dtRounded = dtRounded.AddMinutes(1)
Case RoundTo.Hour
dtRounded = New DateTime(d.Year, _
d.Month, d.Day, d.Hour, 0, 0)
If (d.Minute >= 30) Then _
dtRounded = dtRounded.AddHours(1)
Case RoundTo.Day
dtRounded = New DateTime(d.Year, _
d.Month, d.Day, 0, 0, 0)
If (d.Hour >= 12) Then _
dtRounded = dtRounded.AddDays(1)
End Select
Return dtRounded
End Function
Enum RoundTo
Second
Minute
Hour
Day
End Enum
End Class
KyferEz
May 2, 2010
I made a small mistake. Change the function declaration to:
Shared Function Round(ByVal d As DateTime, _
ByVal rt As RoundTo) As Date
KyferEz
May 2, 2010
thanks alot
iman
May 5, 2010
I’ve added one more thing when copying your code. When creating a new DateTime, you’re not copying the DateKind, which is pretty important in our solution, as DateTime values are otherwise transferred as Localtime over WCF, causing all kinds of mayhem when you’re talking to systems in other timezones.
The change is pretty simple. At the end of each Datetime constructor call, add d.Kind:
tRounded = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, d.Second, d.Kind);
Jesse Houwing
December 9, 2010