Yanıtlar:
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}
DateTime RoundUp(DateTime dt, TimeSpan d) { return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks, dt.Kind); }
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
%d.Ticks
içinde RoundUp
gerekli? d.Ticks - (dt.Ticks % d.Ticks))
mutlaka daha az olacaktır d.Ticks
, bu yüzden cevap aynı olmalı mı?
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);
}
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
2011-08-11 17:00:01
kesildi2011-08-11 17:00:00
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:
DateTime
Yuvarlanacak değeri, TimeSpan
birimlerin tamamını ve kesirli sayısını temsil eden ondalık kayan nokta değerine dönüştürün .Math.Round()
.TimeSpan
birimdeki keneler ile çarparak kenelere geri ölçeklendirin .DateTime
Yuvarlatı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 ;
}
}
DateTime
, ama aynı zamanda yuvarlak yeteneği istediğiniz kadar bir katına unit
. Geçen MidpointRounding.AwayFromZero
üzere Round
istenen etkisi yoktur. MidpointRounding
Tartışmayı kabul ederek aklınızda başka bir şey var mı?
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);
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);
}
/ d.Ticks
en 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.)
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);
}
}
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;
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);
}
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);
}
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?