Can you round a .NET TimeSpan object?

后端 未结 10 957
渐次进展
渐次进展 2020-12-03 03:01

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

10条回答
  •  执念已碎
    2020-12-03 03:42

    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
    

提交回复
热议问题