Can you round a .NET TimeSpan
object?
I have a Timespan
value of: 00:00:00.6193789
Is there a simple way to keep it a TimeSp
Yet another way to round milliseconds to the nearest second.
private const long TicksPer1000Milliseconds = 1000 * TimeSpan.TicksPerMillisecond;
// Round milliseconds to nearest second
// To round up, add the sub-second ticks required to reach the next second
// To round down, subtract the sub-second ticks
elapsedTime = new TimeSpan(elapsedTime.Ticks + (elapsedTime.Milliseconds >= 500 ? TicksPer1000Milliseconds - (elapsedTime.Ticks % TicksPer1000Milliseconds) : -(elapsedTime.Ticks % TicksPer1000Milliseconds)));
Here is a nice Extention-Method:
public static TimeSpan RoundToSeconds(this TimeSpan timespan, int seconds = 1)
{
long offset = (timespan.Ticks >= 0) ? TimeSpan.TicksPerSecond / 2 : TimeSpan.TicksPerSecond / -2;
return TimeSpan.FromTicks((timespan.Ticks + offset) / TimeSpan.TicksPerSecond * TimeSpan.TicksPerSecond);
}
And here are some Examples:
DateTime dt1 = DateTime.Now.RoundToSeconds(); // round to full seconds
DateTime dt2 = DateTime.Now.RoundToSeconds(5 * 60); // round to full 5 minutes
new TimeSpan(tmspan.Hours, tmspan.Minutes, tmspan.Seconds, (int)Math.Round(Convert.ToDouble(tmspan.Milliseconds / 10)));
TimeSpan is little more than a wrapper around the 'Ticks' member. It's pretty easy to create a new TimeSpan from a rounded version of another TimeSpan's Ticks.
TimeSpan t1 = new TimeSpan(2345678);
Console.WriteLine(t1);
TimeSpan t2 = new TimeSpan(t1.Ticks - (t1.Ticks % 100000));
Console.WriteLine(t2);
Gives:
00:00:00.2345678
00:00:00.2300000
My solution:
static TimeSpan RoundToSec(TimeSpan ts)
{
return TimeSpan.FromSeconds((int)(ts.TotalSeconds));
}
Simplest one-liner if you are rounding to whole seconds:
public static TimeSpan RoundSeconds( TimeSpan span ) {
return TimeSpan.FromSeconds( Math.Round( span.TotalSeconds ) );
}
To round to up to 3 digits (e.g. tenths, hundredths of second, or milliseconds:
public static TimeSpan RoundSeconds( TimeSpan span, int nDigits ) {
// TimeSpan.FromSeconds rounds to nearest millisecond, so nDigits should be 3 or less - won't get good answer beyond 3 digits.
Debug.Assert( nDigits <= 3 );
return TimeSpan.FromSeconds( Math.Round( span.TotalSeconds, nDigits ) );
}
For more than 3 digits, its slightly more complex - but still a one-liner. This can also be used for 3 or less digits - it is a replacement for the version shown above:
public static TimeSpan RoundSeconds( TimeSpan span, int nDigits ) {
return TimeSpan.FromTicks( (long)( Math.Round( span.TotalSeconds, nDigits ) * TimeSpan.TicksPerSecond) );
}
If you want a string (according to a comment, this technique only works up to 7 digits):
public static string RoundSecondsAsString( TimeSpan span, int nDigits ) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < nDigits; i++)
sb.Append( "f" );
return span.ToString( @"hh\:mm\:ss\." + sb );
}
For time of day, as hours and minutes, rounded:
public static TimeSpan RoundMinutes(TimeSpan span)
{
return TimeSpan.FromMinutes(Math.Round(span.TotalMinutes));
}
If you have a DateTime, and want to extract time of day rounded:
DateTime dt = DateTime.Now();
TimeSpan hhmm = RoundMinutes(dt.TimeOfDay);
To display rounded 24 hour time:
string hhmmStr = RoundMinutes(dt.TimeOfDay).ToString(@"hh\:mm");
To display time of day in current culture:
string hhmmStr = new DateTime().Add(RoundMinutes(dt.TimeOfDay)).ToShortTimeString();
Credits:
cc1960's answer shows use of FromSeconds, but he rounded to whole seconds. My answer generalizes to specified number of digits.
Ed's answer suggests using a format string, and includes a link to the formatting document.
Chris Marisic shows how to apply ToShortTimeString
to a TimeSpan (by first converting to a DateTime
).
To round to multiple of some other unit, such as 1/30 second:
// Rounds span to multiple of "unitInSeconds".
// NOTE: This will be close to the requested multiple,
// but is not exact when unit cannot be exactly represented by a double.
// e.g. "unitInSeconds = 1/30" isn't EXACTLY 1/30,
// so the returned value won't be exactly a multiple of 1/30.
public static double RoundMultipleAsSeconds( TimeSpan span, double unitInSeconds )
{
return unitInSeconds * Math.Round( span.TotalSeconds / unitInSeconds );
}
public static TimeSpan RoundMultipleAsTimeSpan( TimeSpan span, double unitInSeconds )
{
return TimeSpan.FromTicks( (long)(RoundMultipleAsSeconds( span, unitInSeconds ) * TimeSpan.TicksPerSecond) );
// IF USE THIS: TimeSpan.FromSeconds rounds the result to nearest millisecond.
//return TimeSpan.FromSeconds( RoundMultipleAsSeconds( span, unitInSeconds ) );
}
// Rounds "span / n".
// NOTE: This version might be a hair closer in some cases,
// but probably not enough to matter, and can only represent units that are "1 / N" seconds.
public static double RoundOneOverNAsSeconds( TimeSpan span, double n )
{
return Math.Round( span.TotalSeconds * n ) / n;
}
public static TimeSpan RoundOneOverNAsTimeSpan( TimeSpan span, double n )
{
return TimeSpan.FromTicks( (long)(RoundOneOverNAsSeconds( span, n ) * TimeSpan.TicksPerSecond) );
// IF USE THIS: TimeSpan.FromSeconds rounds the result to nearest millisecond.
//return TimeSpan.FromSeconds( RoundOneOverNAsSeconds( span, n ) );
}
To use one of these to round to multiples of 1/30 second:
private void Test()
{
long ticks = (long) (987.654321 * TimeSpan.TicksPerSecond);
TimeSpan span = TimeSpan.FromTicks( ticks );
TestRound( span, 30 );
TestRound( TimeSpan.FromSeconds( 987 ), 30 );
}
private static void TestRound(TimeSpan span, int n)
{
var answer1 = RoundMultipleAsSeconds( span, 1.0 / n );
var answer2 = RoundMultipleAsTimeSpan( span, 1.0 / n );
var answer3 = RoundOneOverNAsSeconds( span, n );
var answer4 = RoundOneOverNAsTimeSpan( span, n );
}
Results viewed in debugger:
// for 987.654321 seconds:
answer1 987.66666666666663 double
answer2 {00:16:27.6666666} System.TimeSpan
answer3 987.66666666666663 double
answer4 {00:16:27.6666666} System.TimeSpan
// for 987 seconds:
answer1 987 double
answer2 {00:16:27} System.TimeSpan
answer3 987 double
answer4 {00:16:27} System.TimeSpan