Відповіді:
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}
DateTime RoundUp(DateTime dt, TimeSpan d) { return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks, dt.Kind); }
Придумали рішення, яке не передбачає множення та ділення 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
%d.Ticks
в RoundUp
необхідності? d.Ticks - (dt.Ticks % d.Ticks))
обов'язково буде менше d.Ticks
, тому відповідь має бути однаково правильною?
якщо вам потрібно обійти найближчий часовий інтервал (не вгору), то я пропоную скористатися наступним
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);
}
}
Результати:
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
стає усіченим до2011-08-11 17:00:00
Оскільки я ненавиджу винаходити колесо, я, мабуть, дотримуюся цього алгоритму, щоб округлити значення DateTime до визначеного збільшення часу (проміжок часу):
DateTime
значення, яке має бути округлене, у десяткову величину з плаваючою комою, що представляє ціле та дробове число TimeSpan
одиниць.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
аргумент?
Моя версія
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);
Застереження: наведена вище формула є неправильною, тобто наступною:
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);
}
/ d.Ticks
округляється до найближчого 15-хвилинного інтервалу (давайте назвемо ці "блоки"), додавання лише половини блоку не гарантує округлення. Подумайте, коли у вас 4,25 блоків. Якщо ви додасте 0,5 блоків, то перевіряйте, скільки у вас є цілих блоків, у вас все ще є лише 4. Додавання на один галочку менше повного блоку - це правильна дія. Це гарантує вам завжди рухатися до наступного діапазону блоків (перед округленням вниз), але не дозволяє вам рухатися між точними блоками. (IE, якщо ви додали повний блок до 4,0 блоків, 5,0 обходиться до 5, коли ви хочете, щоб 4,99 буде 4).
Більш детальне рішення, яке використовує модуль і уникає зайвих обчислень.
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);
}
}
Це просте рішення округлити до найближчої 1 хвилини. Він зберігає інформацію про TimeZone та Kind DateTime. Він може бути змінений відповідно до ваших власних потреб (якщо вам потрібно обійти найближчі 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;
Ви можете використовувати цей метод, він використовує вказану дату, щоб переконатися, що він підтримує будь-який тип глобалізації та дати, раніше вказаний в об'єкті date date.
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);
}
Якщо ви хочете використовувати 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);
}
var d = new DateTime(2019, 04, 15, 9, 40, 0, 0);
// має бути 9:42, але жоден із цих методів не працює так?