Zamanı en yakın X dakikaya nasıl yuvarlayabilirim?


160

Yuvarlama için basit işlevi var mıdır UP bir DateTimeyakın 15 dakikaya?

Örneğin

2011-08-11 16:59 olur 2011-08-11 17:00

2011-08-11 17:00 olarak kalır 2011-08-11 17:00

2011-08-11 17:01 olur 2011-08-11 17:15

Yanıtlar:


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

Misal:

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
Bu çözüm, onu bir uzantı yöntemi olarak yardımcı program kitaplığım haline getirdi.
JYelton

1
Üst uç noktaya yakın yuvarlama sürelerine dikkat edin. Bu, hesapladığınız Keneler DateTime.MaxValue.Ticks değerinden büyükse bir istisnanın atılmasına neden olabilir. Güvende olun ve hesaplanan değerinizin minimumunu ve DateTime.MaxValue.Ticks değerini alın.
Paul Raff

4
Bu yöntemle DateTime nesnesinden bilgi kaybetmiyor musunuz? Tür ve saat dilimi gibi, ayarlanmışsa?
Evren Kuzucuoglu

11
@ user14 .. (+ d.Ticks - 1) gerekirse yuvarlanmasını sağlar. / Ve * yuvarlanır. Örnek raund 12'den sonraki 5'e: (12 + 5 - 1) = 16,
16/5

12
@dtb küçük bir ek, aksi takdirde muhtemelen biraz DateTime RoundUp(DateTime dt, TimeSpan d) { return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks, dt.Kind); }
rahatsız:

107

Sayıları çarpma ve bölme içermeyen bir çözüm buldumlong .

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

Kullanımı:

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
Bunun çarpma ve bölme kullanmaktan daha hızlı olacağını düşündüm, ancak testlerim bunun olmadığını gösteriyor. Bu 10000000'den fazla yineleme, modül yöntemi benim makinemde ~ 610ms alırken, mult / div yöntemi ~ 500ms aldı. Sanırım FPU'lar yaşlıların endişelerini sorun değil. İşte test kodum: pastie.org/8610460
viggity

1
Uzantıların büyük kullanımı. Teşekkürler!
TravisWhidden

1
@Alovchin Teşekkürler. Cevabı güncelledim. Farkı göstermek için bu ideone kodunuzla oluşturdum: ideone.com/EVKFp5
redent84

1
Bu oldukça eski, ama sonuncu %d.Ticksiçinde RoundUpgerekli? d.Ticks - (dt.Ticks % d.Ticks))mutlaka daha az olacaktır d.Ticks, bu yüzden cevap aynı olmalı mı?
Nate Diamond

1
Sadece belirtmek gerekirse, modül CPU üzerinde bir bölme işlemi gerektirir. Ancak, tamsayı bölümlerinin kaba aşağı özelliğini kullanmanın daha zarif olduğunu kabul ediyorum.
Alex

19

en yakın zaman aralığına (yukarı değil) yuvarlamanız gerekiyorsa, aşağıdakileri kullanmanızı öneririm

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

Bu cevap düzgün yuvarlanmıyor. (soru YUKARI yuvarlama abt çünkü ironik aşağı olarak): user1978424 sadece altındaki en yakın aralığına yuvarlak doğru nasıl gösterileri sonrası vardır
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);
    }
}

Sonuçlar:

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:01kesildi2011-08-11 17:00:00
JYelton

1
@JYelton: + 1'i işaret ettiğiniz için teşekkürler. Kodumu buna uyacak şekilde değiştirdim.
Vlad Bezden

Kolay doğrulama için kod Linqpad biçimini sağlamak harika bir zaman tasarrufu. Kullanımı çok kolay.
Adam Garner

6

Tekerleği yeniden icat etmekten nefret ettiğimden, muhtemelen bir DateTime değerini belirtilen bir zaman artışına (Timespan) yuvarlamak için bu algoritmayı izlerdim:

  • DateTimeYuvarlanacak değeri, TimeSpanbirimlerin tamamını ve kesirli sayısını temsil eden ondalık kayan nokta değerine dönüştürün .
  • Bunu kullanarak bir tamsayıya yuvarlayın Math.Round().
  • Yuvarlatılmış tamsayıyı TimeSpanbirimdeki keneler ile çarparak kenelere geri ölçeklendirin .
  • DateTimeYuvarlatılmış sayıdaki kene sayısından yeni bir değer oluşturun ve bunu çağırana geri döndürün.

İşte kod:

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

}

Bu yuvarlama için güzel bir koddur yakın DateTime , ama aynı zamanda yuvarlak yeteneği istediğiniz kadar bir katına unit . Geçen MidpointRounding.AwayFromZeroüzere Roundistenen etkisi yoktur. MidpointRoundingTartışmayı kabul ederek aklınızda başka bir şey var mı?
HappyNomad

2

Benim versiyonum

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

Bir yöntem olarak bu şekilde kilitlenir

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

ve böyle denir

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

bu saniyeler için geçerli değil
Alex Norcliffe

1

Zarif?

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

1
Daha doğru bir sürüm: x.AddSeconds (900 - (x.AddSeconds (-1) .Minute * 60 + x.AddSeconds (-1) .İkinci)% 900) .AddSeconds (-1), "kalır" koşulu.
Olaf

1

Dikkat: yukarıdaki formül yanlış, yani aşağıdakiler:

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

şu şekilde yeniden yazılmalıdır:

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

1
Katılmıyorum. Tamsayı bölümü / d.Ticksen yakın 15 dakikalık aralığa yuvarlandığından (bu "blokları" diyelim), sadece yarım bir blok eklemek yuvarlamayı garanti etmez. 4.25 bloğunuz olduğunda düşünün. 0,5 blok eklerseniz, kaç tane tamsayı bloğunuz olduğunu test edin, hala sadece 4'ünüz vardır. Tam bir bloktan daha az bir onay işareti eklemek doğru eylemdir. Her zaman bir sonraki blok aralığına (aşağı yuvarlamadan önce) geçmenizi sağlar, ancak kesin bloklar arasında hareket etmenizi önler. (IE, 4.0 bloğa tam bir blok eklediyseniz, 5.0 4.'e yuvarlanır, 4. 4.99 4 olur.)
Brendan Moore

1

Modulo kullanan ve gereksiz hesaplamaları önleyen daha ayrıntılı bir çözüm.

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

Bu, en yakın 1 dakikaya yuvarlamak için basit bir çözümdür. DateTime'ın TimeZone ve Kind bilgilerini korur. Kendi ihtiyaçlarınıza daha uygun olarak değiştirilebilir (en yakın 5 dakikaya yuvarlamanız gerekiyorsa, vb.).

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

Bu yöntemi kullanabilirsiniz, daha önce datetime nesnesinde belirtilen genelleştirme ve datetime türlerinden herhangi birini korumasını sağlamak için belirtilen tarihi kullanır.

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 Fiddle Testi

Yuvarlamak için TimeSpan'ı kullanmak istiyorsanız bunu kullanabilirsiniz.

/// <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 Keman


En yakın 7. dakikaya gitmek isterseniz var d = new DateTime(2019, 04, 15, 9, 40, 0, 0);// 9:42 olmalıdır, ancak bu yöntemlerin hiçbiri böyle çalışmazsa ne olur?
DotnetShadow

Edit @soulflyman cevabı doğru sonucu verir gibi görünüyor
DotnetShadow
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.