時間を最も近いX分に切り上げるにはどうすればよいですか?


160

丸めのための簡単な関数がありUPDateTime最寄りの15分には?

例えば

2011-08-11 16:59 なる 2011-08-11 17:00

2011-08-11 17:00 とどまる 2011-08-11 17:00

2011-08-11 17:01 なる 2011-08-11 17:15

回答:


286
DateTime RoundUp(DateTime dt, TimeSpan d)
{
    return new DateTime((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Kind);
}

例:

var dt1 = RoundUp(DateTime.Parse("2011-08-11 16:59"), TimeSpan.FromMinutes(15));
// dt1 == {11/08/2011 17:00:00}

var dt2 = RoundUp(DateTime.Parse("2011-08-11 17:00"), TimeSpan.FromMinutes(15));
// dt2 == {11/08/2011 17:00:00}

var dt3 = RoundUp(DateTime.Parse("2011-08-11 17:01"), TimeSpan.FromMinutes(15));
// dt3 == {11/08/2011 17:15:00}

13
このソリューションは、拡張メソッドとして私のユーティリティライブラリに入れました。
JYelton 2011

1
上端に近い丸め時間に注意してください。これにより、計算したティックがDateTime.MaxValue.Ticksより大きい場合に例外がスローされる可能性があります。安全であり、計算された値とDateTime.MaxValue.Ticksの最小値を取ってください。
Paul Raff、2014

4
このメソッドを使用して、DateTimeオブジェクトの情報を失っていませんか?種類と時間帯のように、設定されていれば?
Evren Kuzucuoglu 2014

11
@ user14 ..(+ d.Ticks-1)により、必要に応じて切り上げられます。/と*は丸められます。12から次の5への丸めの例:(12 +
5-1)

12
@dtb 1つの小さな追加。それ以外の場合は、おそらく少しバグがあります。日時の種類を維持する必要があります;-) DateTime RoundUp(DateTime dt, TimeSpan d) { return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks, dt.Kind); }
njy

107

数値の乗算除算を 含まないソリューションを考え出しlongます。

public static DateTime RoundUp(this DateTime dt, TimeSpan d)
{
    var modTicks = dt.Ticks % d.Ticks;
    var delta = modTicks != 0 ? d.Ticks - modTicks : 0;
    return new DateTime(dt.Ticks + delta, dt.Kind);
}

public static DateTime RoundDown(this DateTime dt, TimeSpan d)
{
    var delta = dt.Ticks % d.Ticks;
    return new DateTime(dt.Ticks - delta, dt.Kind);
}

public static DateTime RoundToNearest(this DateTime dt, TimeSpan d)
{
    var delta = dt.Ticks % d.Ticks;
    bool roundUp = delta > d.Ticks / 2;
    var offset = roundUp ? d.Ticks : 0;

    return new DateTime(dt.Ticks + offset - delta, dt.Kind);
}

使用法:

var date = new DateTime(2010, 02, 05, 10, 35, 25, 450); // 2010/02/05 10:35:25
var roundedUp = date.RoundUp(TimeSpan.FromMinutes(15)); // 2010/02/05 10:45:00
var roundedDown = date.RoundDown(TimeSpan.FromMinutes(15)); // 2010/02/05 10:30:00
var roundedToNearest = date.RoundToNearest(TimeSpan.FromMinutes(15)); // 2010/02/05 10:30:00

8
これは乗算と除算を使用するよりも速いと確信していましたが、私のテストではそうではないことが示されています。これは10000000回以上の繰り返しであり、mult / divメソッドは〜500msかかりましたが、私のマシンではモジュラスメソッドは〜610msかかりました。FPUは古い問題を問題にしないと思います。ここに私のテストコードは次のとおりです。pastie.org/8610460
viggity

1
拡張機能の活用。ありがとう!
TravisWhidden 2015年

1
@Alovchinありがとう。答えを更新しました。違いを示すために、コードを使用してこのideoneを作成しました:ideone.com/EVKFp5
redent84

1
これはかなり古いですが、最後%d.TicksRoundUp必要ですか?d.Ticks - (dt.Ticks % d.Ticks))は必ずしも未満d.Ticksになるため、答えは同じである必要がありますか?
ネイトダイヤモンド

1
ちょうど指摘したように、係数はCPUでの除算演算を必要とします。しかし、整数除算のルーディングダウンプロパティを使用するよりもエレガントであることに同意します。
アレックス

19

最も近い時間間隔に丸める必要がある場合(アップではない)、次を使用することをお勧めします

    static DateTime RoundToNearestInterval(DateTime dt, TimeSpan d)
    {
        int f=0;
        double m = (double)(dt.Ticks % d.Ticks) / d.Ticks;
        if (m >= 0.5)
            f=1;            
        return new DateTime(((dt.Ticks/ d.Ticks)+f) * d.Ticks);
    }

この答えは正しく丸められません。(質問がUP丸めアプトだったので、皮肉にもダウン投票):user1978424は下記の最寄りの間隔とのラウンドに正確にどのようにショーを投稿している
stittyを

8
void Main()
{
    var date1 = new DateTime(2011, 8, 11, 16, 59, 00);
    date1.Round15().Dump();

    var date2 = new DateTime(2011, 8, 11, 17, 00, 02);
    date2.Round15().Dump();

    var date3 = new DateTime(2011, 8, 11, 17, 01, 23);
    date3.Round15().Dump();

    var date4 = new DateTime(2011, 8, 11, 17, 00, 00);
    date4.Round15().Dump();
}

public static class Extentions
{
    public static DateTime Round15(this DateTime value)
    {   
        var ticksIn15Mins = TimeSpan.FromMinutes(15).Ticks;

        return (value.Ticks % ticksIn15Mins == 0) ? value : new DateTime((value.Ticks / ticksIn15Mins + 1) * ticksIn15Mins);
    }
}

結果:

8/11/2011 5:00:00 PM
8/11/2011 5:15:00 PM
8/11/2011 5:15:00 PM
8/11/2011 5:00:00 PM

3
2011-08-11 17:00:01切り捨てられます2011-08-11 17:00:00
JYelton 2011

1
@JYelton:+1を指摘していただきありがとうございます。私はそれに対応するためにコードを変更しました。
Vlad Bezden、2011

簡単に検証できるようにコードLinqpad形式を提供すると、時間を大幅に節約できます。とても使いやすい。
アダムガーナー

6

ホイールの再発明は嫌いなので、おそらくこのアルゴリズムに従って、DateTime値を指定された時間の増分(Timespan)に丸めます。

  • DateTime丸められる値を、TimeSpan単位の整数および小数を表す10進浮動小数点値に変換します。
  • を使用して、整数に丸めMath.Round()ます。
  • 丸めた整数に単位内のティックの数を掛けて、ティックにスケーリングしTimeSpanます。
  • DateTime丸めたティック数から新しい値をインスタンス化し、呼び出し元に返します。

これがコードです:

public static class DateTimeExtensions
{

    public static DateTime Round( this DateTime value , TimeSpan unit )
    {
        return Round( value , unit , default(MidpointRounding) ) ;
    }

    public static DateTime Round( this DateTime value , TimeSpan unit , MidpointRounding style )
    {
        if ( unit <= TimeSpan.Zero ) throw new ArgumentOutOfRangeException("unit" , "value must be positive") ;

        Decimal  units        = (decimal) value.Ticks / (decimal) unit.Ticks ;
        Decimal  roundedUnits = Math.Round( units , style ) ;
        long     roundedTicks = (long) roundedUnits * unit.Ticks ;
        DateTime instance     = new DateTime( roundedTicks ) ;

        return instance ;
    }

}

これはに丸めるための素晴らしいコードです最寄りの DateTimeが、私はまた、ラウンドに能力たい最大の倍数にしますunit 。に渡しても、望ましい効果は得MidpointRounding.AwayFromZeroられRoundません。MidpointRounding議論を受け入れることで、他に何か心に留めていますか?
HappyNomad 2013年

2

私のバージョン

DateTime newDateTimeObject = oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);

メソッドとしては、このようにロックします

public static DateTime GetNextQuarterHour(DateTime oldDateTimeObject)
{
    return oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
}

そのように呼ばれています

DateTime thisIsNow = DateTime.Now;
DateTime nextQuarterHour = GetNextQuarterHour(thisIsNow);

これは数秒では考慮されません
Alex Norcliffe

1

エレガント?

dt.AddSeconds(900 - (x.Minute * 60 + x.Second) % 900)

1
より正確なバージョンは次のようになります:x.AddSeconds(900-(x.AddSeconds(-1).Minute * 60 + x.AddSeconds(-1).Second)%900).AddSeconds(-1)、 「滞在」状態。
オラフ

1

注意:上記の数式は正しくありません。つまり、次のとおりです。

DateTime RoundUp(DateTime dt, TimeSpan d)
{
    return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
}

次のように書き換える必要があります。

DateTime RoundUp(DateTime dt, TimeSpan d)
{
    return new DateTime(((dt.Ticks + d.Ticks/2) / d.Ticks) * d.Ticks);
}

1
同意しません。整数の除算/ d.Ticksは最も近い15分の間隔に切り捨てられるため(これらを「ブロック」と呼びましょう)、半分のブロックのみを追加しても切り上げは保証されません。4.25ブロックがある場合を検討してください。0.5ブロックを追加した場合、整数ブロックの数をテストしても、まだ4しかありません。完全なブロックよりも1ティック少ないティックを追加するのが正しいアクションです。これにより、常に(切り捨てる前に)次のブロック範囲に移動できますが、正確なブロック間を移動することはできません。(IE、フルブロックを4.0ブロックに追加した場合、5.0は5に丸められ、4が必要な場合は4.99が4になります。)
ブレンダンムーア14年

1

モジュロを使用し、不要な計算を回避する、より詳細なソリューション。

public static class DateTimeExtensions
{
    public static DateTime RoundUp(this DateTime dt, TimeSpan ts)
    {
        return Round(dt, ts, true);
    }

    public static DateTime RoundDown(this DateTime dt, TimeSpan ts)
    {
        return Round(dt, ts, false);
    }

    private static DateTime Round(DateTime dt, TimeSpan ts, bool up)
    {
        var remainder = dt.Ticks % ts.Ticks;
        if (remainder == 0)
        {
            return dt;
        }

        long delta;
        if (up)
        {
            delta = ts.Ticks - remainder;
        }
        else
        {
            delta = -remainder;
        }

        return dt.AddTicks(delta);
    }
}

0

これは、最も近い1分に切り上げる単純なソリューションです。これは、DateTimeのTimeZoneおよびKind情報を保持します。これは、さらに自分のニーズに合わせて変更できます(最も近い5分に丸める必要がある場合など)。

DateTime dbNowExact = DateTime.Now;
DateTime dbNowRound1 = (dbNowExact.Millisecond == 0 ? dbNowExact : dbNowExact.AddMilliseconds(1000 - dbNowExact.Millisecond));
DateTime dbNowRound2 = (dbNowRound1.Second == 0 ? dbNowRound1 : dbNowRound1.AddSeconds(60 - dbNowRound1.Second));
DateTime dbNow = dbNowRound2;

0

このメソッドを使用できます。指定された日付を使用して、以前に日時オブジェクトで指定されたグローバリゼーションと日時の種類を維持します。

const long LNG_OneMinuteInTicks = 600000000;
/// <summary>
/// Round the datetime to the nearest minute
/// </summary>
/// <param name = "dateTime"></param>
/// <param name = "numberMinutes">The number minute use to round the time to</param>
/// <returns></returns>        
public static DateTime Round(DateTime dateTime, int numberMinutes = 1)
{
    long roundedMinutesInTicks = LNG_OneMinuteInTicks * numberMinutes;
    long remainderTicks = dateTime.Ticks % roundedMinutesInTicks;
    if (remainderTicks < roundedMinutesInTicks / 2)
    {
        // round down
        return dateTime.AddTicks(-remainderTicks);
    }

    // round up
    return dateTime.AddTicks(roundedMinutesInTicks - remainderTicks);
}

.Netフィドルテスト

TimeSpanを使用して丸める場合は、これを使用できます。

/// <summary>
/// Round the datetime
/// </summary>
/// <example>Round(dt, TimeSpan.FromMinutes(5)); => round the time to the nearest 5 minutes.</example>
/// <param name = "dateTime"></param>
/// <param name = "roundBy">The time use to round the time to</param>
/// <returns></returns>        
public static DateTime Round(DateTime dateTime, TimeSpan roundBy)
{            
    long remainderTicks = dateTime.Ticks % roundBy.Ticks;
    if (remainderTicks < roundBy.Ticks / 2)
    {
        // round down
        return dateTime.AddTicks(-remainderTicks);
    }

    // round up
    return dateTime.AddTicks(roundBy.Ticks - remainderTicks);
}

TimeSpan Fiddle


最も近い7分に四捨五入したい場合はどうなりvar d = new DateTime(2019, 04, 15, 9, 40, 0, 0);ますか。
DotnetShadow

@soulflymanの回答が正しい結果を生成するように編集してください
DotnetShadow
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.