Risposte:
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Kind);
}
Esempio:
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); }
È venuto con una soluzione che non comporta moltiplicare e dividere i long
numeri.
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);
}
Uso:
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
in RoundUp
necessaria? d.Ticks - (dt.Ticks % d.Ticks))
sarà necessariamente inferiore a d.Ticks
, quindi la risposta dovrebbe essere la stessa corretta?
se è necessario arrotondare all'intervallo di tempo più vicino (non in alto), suggerisco di utilizzare quanto segue
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);
}
}
risultati:
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
viene troncato a2011-08-11 17:00:00
Dato che odio reinventare la ruota, probabilmente seguirò questo algoritmo per arrotondare un valore DateTime a un determinato incremento di tempo (Timespan):
DateTime
valore da arrotondare a un valore decimale in virgola mobile che rappresenta il numero intero e frazionario di TimeSpan
unità.Math.Round()
.TimeSpan
nell'unità.DateTime
valore dal numero arrotondato di tick e restituiscilo al chiamante.Ecco il codice:
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
, ma voglio anche la capacità di turno fino a un multiplo di unit
. Il passaggio MidpointRounding.AwayFromZero
a Round
non ha l'effetto desiderato. Hai in mente qualcos'altro accettando una MidpointRounding
discussione?
La mia versione
DateTime newDateTimeObject = oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
Come metodo si bloccherebbe in questo modo
public static DateTime GetNextQuarterHour(DateTime oldDateTimeObject)
{
return oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
}
e si chiama così
DateTime thisIsNow = DateTime.Now;
DateTime nextQuarterHour = GetNextQuarterHour(thisIsNow);
Attenzione: la formula sopra è errata, ovvero la seguente:
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
}
dovrebbe essere riscritto come:
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime(((dt.Ticks + d.Ticks/2) / d.Ticks) * d.Ticks);
}
/ d.Ticks
viene arrotondata per difetto all'intervallo di 15 minuti più vicino (chiamiamo questi "blocchi"), l'aggiunta di un solo mezzo blocco non garantisce l'arrotondamento. Considera quando hai 4,25 blocchi. Se aggiungi 0,5 blocchi, quindi testa quanti blocchi interi hai, ne hai ancora 4. L'aggiunta di un segno di spunta in meno di un blocco completo è l'azione corretta. Ti assicura di spostarti sempre verso l'intervallo di blocchi successivo (prima di arrotondare per difetto), ma ti impedisce di spostarti tra i blocchi esatti. (IE, se hai aggiunto un blocco completo a 4.0 blocchi, 5.0 dovrebbe arrotondare a 5, quando vuoi 4. 4.99 saranno 4.)
Una soluzione più dettagliata, che utilizza il modulo ed evita calcoli inutili.
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);
}
}
Questa è una soluzione semplice per arrotondare al minuto più vicino. Conserva le informazioni TimeZone e Kind del DateTime. Può essere modificato per soddisfare ulteriormente le proprie esigenze (se è necessario arrotondare ai 5 minuti più vicini, ecc.).
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;
È possibile utilizzare questo metodo, utilizza la data specificata per assicurarsi che mantenga qualsiasi tipo di globalizzazione e datetime precedentemente specificato nell'oggetto datetime.
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);
}
Se vuoi usare TimeSpan per arrotondare, puoi usare questo.
/// <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);
// più vicino dovrebbe essere 9:42 ma nessuno di questi metodi funziona in questo modo?