27

.NETTimeSpanオブジェクトを丸めることはできますか?

私は次のTimespan値を持っています: 00:00:00.6193789

TimeSpanオブジェクトを保持し、
00:00:00.62のように丸める簡単な方法はありますか?

4

10 に答える 10

35

申し訳ありませんが、これまでの質問と一般的な回答の両方が間違っています:-)

Tyndall は丸め方を要求しますが、切り捨ての例を示しているため、この質問は間違っています。

ディーンの答えは、丸めではなく切り捨てにも対処しているため、間違っています。(2つの質問のうちの1つに対する答えが正しいと主張する人もいると思いますが、哲学はさておき...)

丸めの簡単なテクニックを次に示します。

int precision = 2; // Specify how many digits past the decimal point
TimeSpan t1 = new TimeSpan(19365678); // sample input value

const int TIMESPAN_SIZE = 7; // it always has seven digits
// convert the digitsToShow into a rounding/truncating mask
int factor = (int)Math.Pow(10,(TIMESPAN_SIZE - precision));

Console.WriteLine("Input: " + t1);
TimeSpan truncatedTimeSpan = new TimeSpan(t1.Ticks - (t1.Ticks % factor));
Console.WriteLine("Truncated: " + truncatedTimeSpan);
TimeSpan roundedTimeSpan =
    new TimeSpan(((long)Math.Round((1.0*t1.Ticks/factor))*factor));
Console.WriteLine("Rounded: " + roundedTimeSpan);

サンプル コードの入力値と桁数を使用すると、出力は次のようになります。

Input: 00:00:01.9365678
Truncated: 00:00:01.9300000
Rounded: 00:00:01.9400000

精度を 2 桁から 5 桁に変更し、代わりにこれを取得します。

Input: 00:00:01.9365678
Truncated: 00:00:01.9365600
Rounded: 00:00:01.9365700

さらに、この結果を得るために 0 に変更します。

Input: 00:00:01.9365678
Truncated: 00:00:01
Rounded: 00:00:02

最後に、出力をもう少し制御したい場合は、書式を追加します。以下は、表示桁数から精度を分離できることを示す一例です。精度は再び 2 に設定されますが、書式制御文字列の最後の引数で指定されているように、3 桁が表示されます。

Console.WriteLine("Rounded/formatted: " + 
  string.Format("{0:00}:{1:00}:{2:00}.{3:000}",
      roundedTimeSpan.Hours, roundedTimeSpan.Minutes,
      roundedTimeSpan.Seconds, roundedTimeSpan.Milliseconds));
// Input: 00:00:01.9365678
// Truncated: 00:00:01.9300000
// Rounded: 00:00:01.9400000
// Rounded/formatted: 00:00:01.940

2010.01.06 更新: すぐに使えるソリューション

上記の資料は、アイデアを探している場合に役立ちます。それ以来、すぐに使えるコードを探している人のために、パッケージ化されたソリューションを実装する時間がありました。

これはコメントされていないコードであることに注意してください。XML-doc-comments を含む完全にコメントされたバージョンは、四半期の終わりまでに私のオープン ソース ライブラリで利用できるようになります。このように「そのまま」投稿することを躊躇しましたが、興味のある読者にとってはまだ何らかの利益をもたらす可能性があると考えています.

このコードは、上記のコードを改良したもので、丸められたものの、ゼロで埋められた 7 桁を示していました。この完成したバージョンは、指定された桁数に丸められてトリムされます。

呼び出しの例を次に示します。

Console.Write(new RoundedTimeSpan(19365678, 2).ToString());
// Result = 00:00:01.94

完全な RoundedTimeSpan.cs ファイルは次のとおりです。

using System;

namespace CleanCode.Data
{
    public struct RoundedTimeSpan
    {

        private const int TIMESPAN_SIZE = 7; // it always has seven digits

        private TimeSpan roundedTimeSpan;
        private int precision;

        public RoundedTimeSpan(long ticks, int precision)
        {
            if (precision < 0) { throw new ArgumentException("precision must be non-negative"); }
            this.precision = precision;
            int factor = (int)System.Math.Pow(10, (TIMESPAN_SIZE - precision));

            // This is only valid for rounding milliseconds-will *not* work on secs/mins/hrs!
            roundedTimeSpan = new TimeSpan(((long)System.Math.Round((1.0 * ticks / factor)) * factor));
        }

        public TimeSpan TimeSpan { get { return roundedTimeSpan; } }

        public override string ToString()
        {
            return ToString(precision);
        }

        public string ToString(int length)
        { // this method revised 2010.01.31
            int digitsToStrip = TIMESPAN_SIZE - length;
            string s = roundedTimeSpan.ToString();
            if (!s.Contains(".") && length == 0) { return s; }
            if (!s.Contains(".")) { s += "." + new string('0', TIMESPAN_SIZE); }
            int subLength = s.Length - digitsToStrip;
            return subLength < 0 ? "" : subLength > s.Length ? s : s.Substring(0, subLength);
        }
    }
}

2010.02.01 UPDATE: パッケージ化されたソリューションが利用可能になりました

昨日、上記の RoundedTimeSpan を含め、予想よりも早く、オープンソース ライブラリの新しいバージョンをリリースしました。コードはこちらです。API の場合は、ここから始めて名前空間RoundedTimeSpanの下に移動します。CleanCode.DataCleanCode.DLL ライブラリには上記のコードが含まれていますが、完成したパッケージで提供されます。ToString(int)2010.01.06 に投稿して以来、上記の方法を少し改善したことに注意してください。

于 2010-01-05T19:36:48.727 に答える
22

整数秒に丸めている場合の最も単純なワンライナー:

public static TimeSpan RoundSeconds( TimeSpan span ) {
    return TimeSpan.FromSeconds( Math.Round( span.TotalSeconds ) );
}

3 桁までに丸めるには (例: 10 分の 1、100 分の 1 秒、またはミリ秒) :

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

3 桁を超える場合は、少し複雑になりますが、それでもワンライナーです。これは、3 桁以下の数字にも使用できます。これは、上記のバージョンの代替品です。

public static TimeSpan RoundSeconds( TimeSpan span, int nDigits ) {
    return TimeSpan.FromTicks( (long)( Math.Round( span.TotalSeconds, nDigits ) * TimeSpan.TicksPerSecond) );
}

文字列が必要な場合(コメントによると、この手法は最大7桁までしか機能しません):

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

time of day の場合は、時間ととして丸められます。

public static TimeSpan RoundMinutes(TimeSpan span)
    {
        return TimeSpan.FromMinutes(Math.Round(span.TotalMinutes));
    }

DateTime があり、丸められた時刻を抽出したい場合:

DateTime dt = DateTime.Now();
TimeSpan hhmm = RoundMinutes(dt.TimeOfDay);

丸められた 24 時間制を表示するには:

string hhmmStr = RoundMinutes(dt.TimeOfDay).ToString(@"hh\:mm");

現在のカルチャで時刻を表示するには:

string hhmmStr = new DateTime().Add(RoundMinutes(dt.TimeOfDay)).ToShortTimeString();

クレジット:

cc1960 の回答は FromSeconds の使用を示していますが、彼は整数秒に丸めました。私の答えは、指定された桁数に一般化されます。

Ed の回答では、書式文字列の使用が提案されており、書式設定ドキュメントへのリンクが含まれています。

Chris Marisicが TimeSpanに適用ToShortTimeStringする方法を示します (最初に に変換しますDateTime)。


1/30 秒など、他の単位の倍数に丸めるには:

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

これらのいずれかを使用して 1/30 秒の倍数に丸めるには:

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

デバッガーで表示される結果:

// 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
于 2016-09-28T09:20:47.527 に答える
17

TimeSpan は、'Ticks' メンバーのラッパーにすぎません。別の TimeSpan のティックの丸められたバージョンから新しい TimeSpan を作成するのは非常に簡単です。

TimeSpan t1 = new TimeSpan(2345678);
Console.WriteLine(t1);
TimeSpan t2 = new TimeSpan(t1.Ticks - (t1.Ticks % 100000));
Console.WriteLine(t2);

与えます:

00:00:00.2345678
00:00:00.2300000
于 2008-12-03T21:12:32.510 に答える
7

秒への丸めに関するいくつかのコメントを考えると、任意の TimeSpan への丸めが良いと思いました。

public static TimeSpan Round(this TimeSpan ts, TimeSpan rnd) {
    if (rnd == TimeSpan.Zero)
        return ts;
    else {
        var rndTicks = rnd.Ticks;
        var ansTicks = ts.Ticks + Math.Sign(ts.Ticks) * rndTicks / 2;
        return TimeSpan.FromTicks(ansTicks - ansTicks % rndTicks);
    }
}
public static TimeSpan Round(this TimeSpan ts) => ts.Round(TimeSpan.FromSeconds(1));

(@ToolmakerSteve ごとに) 分数単位を扱うときに目盛りに丸められる可能性のある不正確さを考慮して、より高い精度が必要で、コンピューターの小数秒に丸められる場合に小数丸めオプションを追加します。

public static TimeSpan RoundToFraction(this TimeSpan ts, long num, long den) => (den == 0.0) ? TimeSpan.Zero : TimeSpan.FromTicks((long)Math.Round(Math.Round((double)ts.Ticks * (double)den / num / TimeSpan.TicksPerSecond) * (double)num / den * TimeSpan.TicksPerSecond));
public static TimeSpan RoundToFraction(this TimeSpan ts, long den) => (den == 0.0) ? TimeSpan.Zero : TimeSpan.FromTicks((long)(Math.Round((double)ts.Ticks * den / TimeSpan.TicksPerSecond) / den * TimeSpan.TicksPerSecond));
于 2016-12-16T23:17:22.943 に答える
3
new TimeSpan(tmspan.Hours, tmspan.Minutes, tmspan.Seconds, (int)Math.Round(Convert.ToDouble(tmspan.Milliseconds / 10)));
于 2008-12-03T21:11:11.763 に答える
1

TimeSpan についてはわかりませんが、DateTimes に関する次の投稿を確認してください:
http://mikeinmadison.wordpress.com/2008/03/12/datetimeround/

于 2008-12-03T21:05:50.667 に答える
1

私の解決策:

    static TimeSpan RoundToSec(TimeSpan ts)
    {
        return TimeSpan.FromSeconds((int)(ts.TotalSeconds));
    }
于 2016-06-09T10:05:43.710 に答える
0

ミリ秒を最も近い秒に丸めるさらに別の方法。

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)));
于 2014-06-26T12:18:11.153 に答える