Réponses:
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime((dt.Ticks + d.Ticks - 1) / d.Ticks * d.Ticks, dt.Kind);
}
Exemple:
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); }
J'ai trouvé une solution qui n'implique pas de multiplier et de diviser les long
nombres.
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);
}
Usage:
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
en RoundUp
nécessaire? d.Ticks - (dt.Ticks % d.Ticks))
sera nécessairement inférieur à d.Ticks
, donc la réponse devrait être la même correcte?
si vous avez besoin d'arrondir à un intervalle de temps le plus proche (pas supérieur), je suggère d'utiliser ce qui suit
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);
}
}
Résultats:
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
obtient tronqué à2011-08-11 17:00:00
Puisque je déteste réinventer la roue, je suivrais probablement cet algorithme pour arrondir une valeur DateTime à un incrément de temps spécifié (Timespan):
DateTime
valeur à arrondir en une valeur décimale à virgule flottante représentant le nombre entier et fractionnaire d' TimeSpan
unités.Math.Round()
.TimeSpan
unité.DateTime
valeur à partir du nombre arrondi de graduations et renvoyez-la à l'appelant.Voici le code:
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
, mais je veux aussi la capacité à arrondir jusqu'à un multiple de unit
. Passer MidpointRounding.AwayFromZero
à Round
n'a pas l'effet escompté. Avez-vous autre chose en tête en acceptant un MidpointRounding
argument?
Ma version
DateTime newDateTimeObject = oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
En tant que méthode, il se verrouillerait comme ça
public static DateTime GetNextQuarterHour(DateTime oldDateTimeObject)
{
return oldDateTimeObject.AddMinutes(15 - oldDateTimeObject.Minute % 15);
}
et s'appelle comme ça
DateTime thisIsNow = DateTime.Now;
DateTime nextQuarterHour = GetNextQuarterHour(thisIsNow);
Attention: la formule ci-dessus est incorrecte, c'est à dire la suivante:
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime(((dt.Ticks + d.Ticks - 1) / d.Ticks) * d.Ticks);
}
devrait être réécrit comme:
DateTime RoundUp(DateTime dt, TimeSpan d)
{
return new DateTime(((dt.Ticks + d.Ticks/2) / d.Ticks) * d.Ticks);
}
/ d.Ticks
arrondit à l'intervalle de 15 minutes le plus proche (appelons ces "blocs"), l'ajout d'un demi-bloc ne garantit pas l'arrondi vers le haut. Considérez quand vous avez 4,25 blocs. Si vous ajoutez 0,5 bloc, puis testez le nombre de blocs entiers que vous avez, vous n'en avez toujours que 4. Ajouter une coche de moins qu'un bloc complet est l'action correcte. Cela garantit que vous passez toujours à la plage de blocs suivante (avant d'arrondir vers le bas), mais vous empêche de vous déplacer entre les blocs exacts. (IE, si vous avez ajouté un bloc complet à 4,0 blocs, 5,0 arrondirait à 5, quand vous voulez 4. 4,99 sera 4.)
Une solution plus verbeuse, qui utilise modulo et évite les calculs inutiles.
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);
}
}
Il s'agit d'une solution simple pour arrondir à la minute la plus proche. Il préserve les informations TimeZone et Kind de DateTime. Il peut être modifié en fonction de vos propres besoins (si vous devez arrondir aux 5 minutes les plus proches, etc.).
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;
Vous pouvez utiliser cette méthode, elle utilise la date spécifiée pour garantir qu'elle conserve l'un des types de globalisation et de datetime précédemment spécifiés dans l'objet 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);
}
Si vous souhaitez utiliser TimeSpan pour arrondir, vous pouvez l'utiliser.
/// <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);
// devrait être 9:42 mais aucune de ces méthodes ne fonctionne comme ça?