web-dev-qa-db-fra.com

Arrondir des objets DateTime

Je veux arrondir les dates/heures à l'intervalle le plus proche pour une application graphique. Je voudrais une signature de méthode d'extension comme suit afin que l'arrondi puisse être obtenu pour n'importe quel niveau de précision:

static DateTime Round(this DateTime date, TimeSpan span);

L'idée est que si je passe dans un intervalle de temps de dix minutes, il arrondira à l'intervalle de dix minutes le plus proche. Je n'arrive pas à comprendre l'implémentation et j'espère que l'un de vous aura déjà écrit ou utilisé quelque chose de similaire auparavant.

Je pense que soit un sol, un plafond ou une mise en œuvre la plus proche est très bien.

Des idées?

Edit: Grâce à @tvanfosson & @ShuggyCoUk, l'implémentation ressemble à ceci:

public static class DateExtensions {
    public static DateTime Round(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Floor(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks / span.Ticks);
        return new DateTime(ticks * span.Ticks);
    }
    public static DateTime Ceil(this DateTime date, TimeSpan span) {
        long ticks = (date.Ticks + span.Ticks - 1) / span.Ticks;
        return new DateTime(ticks * span.Ticks);
    }
}

Et s'appelle ainsi:

DateTime nearestHour = DateTime.Now.Round(new TimeSpan(1,0,0));
DateTime minuteCeiling = DateTime.Now.Ceil(new TimeSpan(0,1,0));
DateTime weekFloor = DateTime.Now.Floor(new TimeSpan(7,0,0,0));
...

À votre santé!

100
grenade

Sol

long ticks = date.Ticks / span.Ticks;

return new DateTime( ticks * span.Ticks );

Arrondi (au milieu)

long ticks = (date.Ticks + (span.Ticks / 2) + 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );

Plafond

long ticks = (date.Ticks + span.Ticks - 1)/ span.Ticks;

return new DateTime( ticks * span.Ticks );
121
tvanfosson

Cela vous permettra d'arrondir à n'importe quel intervalle donné. C'est aussi légèrement plus rapide que de diviser puis de multiplier les ticks.

public static class DateTimeExtensions
{
  public static DateTime Floor(this DateTime dateTime, TimeSpan interval)
  {
    return dateTime.AddTicks(-(dateTime.Ticks % interval.Ticks));
  }

  public static DateTime Ceiling(this DateTime dateTime, TimeSpan interval)
  {
    var overflow = dateTime.Ticks % interval.Ticks;

    return overflow == 0 ? dateTime : dateTime.AddTicks(interval.Ticks - overflow);
  }

  public static DateTime Round(this DateTime dateTime, TimeSpan interval)
  {
    var halfIntervalTicks = (interval.Ticks + 1) >> 1;

    return dateTime.AddTicks(halfIntervalTicks - ((dateTime.Ticks + halfIntervalTicks) % interval.Ticks));
  }
}
35
aj.toulan

Vous devez également être clair si vous voulez que votre arrondi:

  1. être au début, à la fin ou au milieu de l'intervalle
    • le démarrage est le plus simple et souvent le plus attendu, mais vous devez être clair dans vos spécifications initiales.
  2. Comment vous souhaitez arrondir les cas limites.
    • normalement seulement un problème si vous arrondissez au milieu plutôt qu'à la fin.
    • Étant donné que l'arrondi au milieu est une tentative de réponse sans biais, vous devez utiliser quelque chose comme Arrondi des banquiers techniquement arrondir la moitié, même pour être vraiment exempt de biais.

Il est très probable que vous ne vous souciez vraiment que du premier point, mais dans ces questions `` simples '', le comportement résultant peut avoir des conséquences d'une grande portée lorsque vous l'utilisez dans le monde réel (souvent à des intervalles adjacents à zéro)

la solution de tvanfosson couvre tous les cas répertoriés en 1. L'exemple du milieu est biaisé vers le haut. Il est douteux que ce soit un problème d'arrondi lié au temps.

10
ShuggyCoUk

Utilisez simplement les tiques, en utilisant cela pour diviser, plancher/plafond/arrondir la valeur et multipliez-la.

2
Lucero